# 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 templates, and you'll be able to modify it to your own preferences by dragging and dropping these items 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 via quick search, either by typing a product name or using the “Templates” filter option. Quick search can be opened in Templates mode using the keyboard shortcut `T`.

![](https://www.datocms-assets.com/55802/1779448479-screenshot-2026-05-22-at-12-14-11.png)

#### Adding & Removing actions

When inside of the quick search popup, simply drag and drop any action or template into the toolbar.

[Video](https://stream.mux.com/2slZhYhrQkMVuEmMojII3g3jbUVbUEng/high.mp4)

### 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.

[Video](https://stream.mux.com/epmaOnbRdohqQi02llzfeXdUyyoAX001uO/high.mp4)

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.

[Video](https://stream.mux.com/ul01Vj9JJaP5V2yl8I2zBJrCsbkRBFEES/high.mp4)

### 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.

[Video](https://stream.mux.com/tZ02xB6yNQSND8ftKIVdZiSW01cAMXWn6j/high.mp4)

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.

[Video](https://stream.mux.com/l5ttuqQW8H1Qiv9sgUJqKaLTwOBJ8uuG/high.mp4)

### 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.

### Story duplication

#### Overview

Story duplication lets you create a copy of an existing story. It does not carry over any event data.

#### How to duplicate a story

**From the story list:**

![](https://www.datocms-assets.com/55802/1780489983-screenshot-2026-06-03-at-13-32-28.png)

**From inside a story:**

![](https://www.datocms-assets.com/55802/1780491216-screenshot-2026-06-03-at-13-53-16.png)

> **NOTE:**
> If either of the following conditions apply, all actions in the duplicated story will automatically move to a **draft** state:
> 
> -   The team has change control enabled by default
>     
> -   The story being duplicated has change control enabled

### 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/).



[Video](https://stream.mux.com/wyG59HdJ4Z013c7hZii16KaJeM01A6tp900/high.mp4)

### 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, MCP servers 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

[Video](https://stream.mux.com/nvRrWzlgdCp7IBaRj00Afgr1UI3zOH1Dq/high.mp4)

#### 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)

## Requirements on stories

When requirements are enabled for a team/tenant, each story displays a requirements status icon in the change control header — the appearance indicates whether all required conditions are met. More on how to set up requirements [here](https://tines.com/docs/admin/change-control/).

![](https://www.datocms-assets.com/55802/1782486451-screenshot-2026-06-26-at-16-07-16.png)

Clicking the icon opens a panel grouped into three sections: 

- **Required**
- **Recommended**
- **Passed**

Each section is collapsible. Hovering over the info icon on any requirement shows a tooltip with the full detail: which attribute is being checked, the comparison condition, and the configured value.

![](https://www.datocms-assets.com/55802/1782486546-screenshot-2026-06-26-at-16-08-46.png)

## 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. 

[Video](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.

[Video](https://stream.mux.com/Clv9ALKGb15awciqoTj4mihgzuJUVbMS/high.mp4)

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`.

[Video](https://stream.mux.com/AW00aLUoaIsWsmAlQAdRFTYHG2nZdVcGX/high.mp4)

#### 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.

[Video](https://stream.mux.com/VmyBcsJI2aeV1KoUNVNCjJ0296NIxaMXM/high.mp4)

#### 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.

[Video](https://stream.mux.com/5e2CkEp5HmZVxwobsrq00E00JGw1h01Kjlp/high.mp4)

![](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.

[Video](https://stream.mux.com/qdX00301Dt13FqgEtzgbRznsZX4xa00APLr/high.mp4)

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. 

[Video](https://stream.mux.com/4Gg8XU02c02KRKZ4V7xUBDi5LT00U0216BHn/high.mp4)

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`

[Video](https://stream.mux.com/Sp4BdmiSwFkvRwDkRjmppECH702GOxgEy/high.mp4)

### 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.

[Video](https://stream.mux.com/JXGTWbhUCcJpJyy4CO02FFXiHqg02kCyIQ/high.mp4)

### 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.

[Video](https://stream.mux.com/eo00umwcHETyn7i02orJOXlKisGqbH62rF/high.mp4)

### 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.

[Video](https://stream.mux.com/yMVBSAIDvXFl6DGsZthhHQH01yCZnDxAW/high.mp4)

### 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.

[Video](https://stream.mux.com/Ar2NM013Ya3Xm4zIAFGyETcVxBBh5p7fO/high.mp4)

### Workbench for Storyboard

## Introduction

Workbench for Storyboard (formerly [Story Copilot](https://www.tines.com/whats-new/workbench-for-stories/)) is an AI-chat interface for the storyboard. It helps you understand, maintain, manage, and build stories using natural language.

## Use **Workbench for Storyboard**

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

Workbench for Storyboard 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 Workbench for Storyboard are recorded.

### Roles and permissions

Workbench for Storyboard 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 Workbench for Storyboard.

### Models

Workbench for Storyboard 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

Workbench for Storyboard 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.

[Video](https://stream.mux.com/qXNC6Kr02gMRZ02rz5ys5PUKB02eR400DXZt/high.mp4)

## 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.

[Video](https://stream.mux.com/ojQuxgKB1fsTDDXnt9HCw3fsBzRuJc8i/high.mp4)

## 
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](https://stream.mux.com/B3u9W5AspT1oK1Mi02gTe1zcoU2YOuhXG/high.mp4)

## 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](https://stream.mux.com/B3u9W5AspT1oK1Mi02gTe1zcoU2YOuhXG/high.mp4)

### 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.

[Video](https://stream.mux.com/mxMyS5txJtW3zIYx9wNpaoh02x6JFYVuW/high.mp4)

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

[Video](https://stream.mux.com/Wtq00f01ol5jEO023zHpM45HhaIvejSRRT1/high.mp4)

## 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

| Limit | Description | Value |
| --- | --- | --- |
| Max input tokens per minute per tenant | Input tokens allowed per minute per tenant. | 900,000 (eu-west-1) or 1,500,000 (other regions) |
| Max runs per minute per tenant | AI action runs allowed per tenant per minute. | 40 (eu-west-1) or 100 (other regions) |
| AI provider max runs per minute per tenant | Runs to external AI providers allowed per tenant per minute. | 500 |

## 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. 

[Video](https://stream.mux.com/8wW9bReLTE02Q1r8fHJCZfIkyEb9MC7RL/high.mp4)

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. 

[Video](https://stream.mux.com/OabX7hJL00UEd51aQiMVD6t01NPqOUyfPa/high.mp4)

### 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.  Finally, you can create [private templates using Workbench for Storyboard](https://www.tines.com/docs/actions/templates/private-templates/#private-templates-via-workbench-for-storyboard) if you have the relevant API spec available. 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.

[Video](https://stream.mux.com/nMjatiVcbfK02htNz4agwg3ZrsrpcKOXI/high.mp4)

#### 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 toolbar search on the storyboard, just like [public templates](https://www.tines.com/docs/templates/). Click the quick search icon on the toolbar 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 Workbench for Storyboard

[Workbench for Storyboard](https://www.tines.com/docs/stories/workbench-for-storyboard/) can manage your tenant’s private template library directly from the chat. In **build** mode, you can ask Workbench for Storyboard 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, Workbench for Storyboard 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 Workbench for Storyboard will parse and navigate them section by section. Instead of manually copying and pasting large code blocks into the chat, you can simply upload your file and prompt the Workbench for Storyboard: *“Here’s our internal API spec, please create private templates for each endpoint.”*

> **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-11-25` 
  
  - 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 a request is authenticated with a Tines API key or OAuth, the authenticated user's email is available in the emitted event at `<<headers.email>>`, and in linked tool actions at `META.headers.email`. See Request headers in tools and events for details.

### **Request headers in tools and events**

When **Include headers** is enabled on your MCP server (the default), Tines captures the incoming HTTP request headers from each MCP client request. You can use these headers in two places:

- **In downstream actions** connected to the MCP server — reference `<<headers.*>> `from the emitted event (for example, `<<headers.email>>`).
- **In tool actions** linked to the MCP server — reference `META.headers.*` in formulas (for example, `<<META.headers.email>>`).

Headers reflect the MCP request that triggered the current tool call. If a client sends custom headers in its MCP configuration, those headers are available too:

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

In a tool action, `META.headers.x_department` would return security.

**Header names**

Header names follow the same rules as [webhooks](https://www.tines.com/docs/actions/types/webhook/):

**Authenticated requests**

When a request is authenticated with a [Tines API key](https://www.tines.com/api/authentication/) or OAuth, Tines adds the authenticated user's email to the headers as email. You can reference it as:

- `<<headers.email>>` in downstream actions
- `META.headers.email` in tool formulas

For security, the Authorization header is not included in the captured headers when API key or OAuth authentication is used.

**Other available headers**

In addition to headers sent by the MCP client, Tines includes:

- **request_ip** — the client's IP address
- **request-method** — the HTTP method (typically POST)
- **content-type** — the request content type
- **date** — the date the request was received

To disable header capture entirely, turn off **Include headers** in the MCP server configuration. When disabled, neither `<<headers.*>>` nor `META.headers.*` will be available.

### 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><th>Hint</th><th>Description</th><th>Default</th></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.

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).

## Network requirements

No additional ports need to be open apart from the ones to Cloudflare specified [here](/docs/admin/tunnel/#connectivity-requirements).

##### Custom runtimes

## Overview

Custom runtimes allow you to upload your own Python runtime environment to run scripts in Cloud or Self Hosted deployments with full control over dependencies and Python versions. This gives you flexibility beyond the standard Python 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
> 
> | Limit | Value |
> | --- | --- |
> | Maximum uncompressed size | 1 GB |
> | Maximum file size per file | 250 MB |
> | Maximum ZIP entries | 100,000 |
> 
> 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. If on a Cloud deployment, select an Execution Environment: Cloud or Docker.
  
  1. Cloud (the default) is for custom runtimes that are used when running scripts on the AWS Lambda infrastructure managed by Tines.
  2. Docker is when the custom runtime will be used in Run Script actions that run on tines command runner over tunnel. 
4. Select a **primary team** that will own the runtime.
5. Optionally grant access to other teams via the **Access** tab.
6. Click **Add** to submit.

While uploading, you will see a progress indicator in the editor model, and the status will show in the Custom Runtime list view. If the environment was created on Cloud for Cloud execution you will see the status as **Creating... **because we will be doing extraction of the zip to use in Lambda-backed Run Script actions. When extraction completes, the status changes to **Available**. For creating self-hosted custom runtimes or "Docker" custom runtimes for Cloud deployments if the runtime is shown in the list view then it should be available (as extraction comes later, during the action run).

![](https://www.datocms-assets.com/55802/1783014221-screenshot-2026-07-02-at-1-43-28-pm.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. If you are planning to run your custom runtime over tunnel, you will need to add the **Implementation for Run Script **action option and select **Docker **before your Docker custom runtimes will be available to select.

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

### Self-hosted architecture requirements

> **WARNING:**
> Tines-command-runner (our application for running Run Script actions on self-hosted infrastructure) **tracks important information locally** for scripts using custom runtimes.
> 
> Running actions with custom runtimes on a tines-command-runner deployment with multiple containers can lead to **unpredictable job failures** if your installation is not appropriately configured.
> 
> See our tines-command-runner [configuration reference](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#multi-container-deployments) for instructions to ensure that your Tines environment can properly handle traffic for custom runtime executions.

### 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'

#### Self-hosted file paths

Custom runtimes in self-hosted Tines instances are stored on the same server or container you use to run Tines Command Runner. Relative paths work the same as in Cloud: your runtime runs actions in its working directory. Actions in one runtime can't see into any other runtime's files, and run under a dedicated user granted access to only files in that working directory.

By default, runtimes live under `/tmp/tcr-harnesses`. You can tell Tines Command Runner to keep runtimes somewhere else by setting the `TCR_HARNESS_DIR` environment variable to another path on your server.

## 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.

#### 
Building custom runtimes for running on Tines Command Runner (self-hosted and Cloud + Docker runtimes)

On self-hosted and Cloud with Run Script over tunnel deployments, custom runtimes run wherever your Tines Command Runner service runs. Make sure that the architecture you use for building custom runtimes matches the one Tines Command Runner is using. On Linux and MacOS servers, you can check your system's architecture by running the command `uname -m` on the server. 

### 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 -r /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 on AWS Lambda Infrastructure

### 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

*This feature is available only for custom runtimes in Cloud deployments running the Cloud Implementation action option.*

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 the right architecture. **That's **Amazon Linux 2023** for Cloud runtimes, and the architecture of Tines Command Runner for self-hosted. This ensures GLIBC compatibility with the system runtime.
5. **Test with the same architecture.** Build and test with `--platform linux/amd64` to match Lambda's x86_64 architecture, or a matching platform (e.g. `--platform linux/arm64`) for self-hosted.

#### 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 and 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 for docker compose](https://www.tines.com/docs/self-hosted/deploying-tines/docker-compose/run-script-setup/) [tines-command-runner documentation for AWS Fargate](https://www.tines.com/docs/self-hosted/deploying-tines/aws-fargate/run-script-setup/)  
  
[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 (with AWS CLI) | 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.

[Video](https://stream.mux.com/csAwbTeXf4hJfamFrdCjk9cifsfH802VP/high.mp4)

### 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.

[Video](https://stream.mux.com/7SUILWVQGpKMvcV00hmmxL3zMurWgo01nx/high.mp4)

### 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.

[Video](https://stream.mux.com/eM00XuM1kb27rFNlH1a4WgEfEV1zJ00C01E/high.mp4)

### 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.

[Video](https://stream.mux.com/NvrMi9gTXpxogmKryFIgsfsdS2QlT6WY/low.mp4)

## 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

| Keyboard shortcut | Description |
| --- | --- |
| 
`⌘ K`

 | Opens the command bar |
| 

`⌘ ⇧ M`

 | Navigates to Workbench |
| 

`⌘ ⇧ E`

 | Search and switch teams |
| 

`⌘ ⇧ K`

 | Toggle light/mode |
| 

`⌘ ⇧ 1`

 | Toggle feature flags |
| 

`⎋`

 | Dismisses any open modals |

### 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><th>Keyboard shortcuts</th><th>Description</th></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

| Keyboard shortcuts | Descriptions |
| --- | --- |
| `⌘ ↵` | Submit page (when there is only one button on the page) |

### Notable Cases shortcuts

See cases shortcuts [here](https://www.tines.com/docs/cases/overview/shortcuts/).

### Notable Workbench shortcuts

| Keyboard shortcuts | Descriptions |
| --- | --- |
| `N` | Starts a new chat |
| `/` | Focuses on the chat input |
| `⌘ [` | Toggles the left sidebar |

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 24 hours. 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:

| Function | Parameter index where credential can be used |
| --- | --- |
| [AES\_DECRYPT](https://www.tines.com/docs/formulas/functions/aes-decrypt/) | 1 (key) |
| [AES\_ENCRYPT](https://www.tines.com/docs/formulas/functions/aes-encrypt/) | 1 (key) |
| [TINES\_ENCRYPT](https://www.tines.com/docs/formulas/functions/tines-encrypt/) | 1 (key) |
| [TINES\_DECRYPT](https://www.tines.com/docs/formulas/functions/tines-decrypt/) | 1 (key) |
| [OPENSSL\_ENCRYPT](https://www.tines.com/docs/formulas/functions/openssl-encrypt/) | 2 (key) |
| [OPENSSL\_DECRYPT](https://www.tines.com/docs/formulas/functions/openssl-decrypt/) | 2 (key) |
| [RSA\_ENCRYPT](https://www.tines.com/docs/formulas/functions/rsa-encrypt/) | 1 (key) |
| [RSA\_DECRYPT](https://www.tines.com/docs/formulas/functions/rsa-decrypt/) | 1 (key) |
| [RSA\_AES\_HYBRID\_ENCRYPT](https://www.tines.com/docs/formulas/functions/rsa-aes-hybrid-encrypt/) | 1 (key) |
| [RSA\_AES\_HYBRID\_DECRYPT](https://www.tines.com/docs/formulas/functions/rsa-aes-hybrid-decrypt/) | 1 (key) |
| [JWT\_SIGN](https://www.tines.com/docs/formulas/functions/jwt-sign/) | 1 (key) |
| [HMAC\_SHA1](https://www.tines.com/docs/formulas/functions/hmac-sha1/) | 1 (secret) |
| [HMAC\_SHA1\_BASE64](https://www.tines.com/docs/formulas/functions/hmac-sha1-base64/) | 1 (secret) |
| [HMAC\_SHA256](https://www.tines.com/docs/formulas/functions/hmac-sha256/) | 1 (secret) |
| [HMAC\_SHA256\_BASE64](https://www.tines.com/docs/formulas/functions/hmac-sha256-base64/) | 1 (secret) |
| [SIZE](https://www.tines.com/docs/formulas/functions/size/) | 0 (text or array) |
| [ZIP](https://www.tines.com/docs/formulas/functions/zip/) | 2 (password) |
| [UNZIP](https://www.tines.com/docs/formulas/functions/unzip/) | 1 (password) |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Access token |
| Scopes | Allows access to all endpoints in the Abnormal Security API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Auth key |
| Scopes | Allows access to all endpoints in the various Abuse.ch products' APIs |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the AbuseIPDB API |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Secret
-   Data center URL

Select the 'Copy' button to copy the above and paste as one item into the connect flow to be processed. |
| Scopes | 

The API client will have the same privileges as your account has

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Base URL
-   Account ID

 |
| Scopes | The API key generated is limited to the permissions set on the user profile that generated the key.  |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Personal access token |
| Scopes | Defined by the user generating the token. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the AlienVault OTX API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Personal access token
-   Ansible host

 |
| Scopes | Determined by the user permissions associated with the personal access token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Anvilogic API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the ANY.RUN API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the APIVoid API |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Secret key
-   Base URL

 |
| Scopes | Allows access to all endpoints in the Armis Security API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Asana |
| Scopes | *default*: provides access to all endpoints documented in Asana's [API reference](https://developers.asana.com/reference/rest-api-reference) |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client secret

 |
| Scopes | Allows access to all endpoints in the Astrix API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | AWS |
| Required inputs | 
-   Role ARN
-   Credential name
-   ID of team to create credential in

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. |
| Scopes | Allows access to run any AWS CLI command that your selected IAM role has permissions to run. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   API secret
-   Hostname

 |
| Scopes | Allows access to all endpoints in the Axonius API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   BambooHR API key
-   BambooHR domain

 |
| Scopes | Defined by the user generating the API key. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Bayse.io API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Application (client) ID
-   Client secret value

 |
| Scopes | Allows access to all endpoints in the Black Kite API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | At various steps you will be prompted to enter the following:  
-   API Key
-   ID
-   Domain

 |
| Scopes | Determined by the permissions of the associated user, personal or non-personal |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Box |
| Scopes | 
-   Read all files and folders stored in Box.
-   Write all files and folders stored in Box.

Access to content is further restricted by the authenticating users' permissions. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the Bugcrowd API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   API key
-   Tenant name
-   Tenant region

 |
| Scopes | Determined by the user creating the API key. 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' |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   API key
-   API key secret

 |
| Scopes | Defined by the user generating the API key. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Defined by the user generating the API token. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key

 |
| Scopes | Access depends on the permissions available to the associated users Claude profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Application (client) ID
-   Client secret value

 |
| Scopes | Allows access to all endpoints in the ClickUp API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Account ID

 |
| Scopes | Defined by the user generating the API token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Access key
-   Console domain

 |
| Scopes | Determined by the user creating the access key. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client Secret
-   Base URL

 |
| Scopes | Scopes are determined by the user creating the API client |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Account email associated with the API key
-   Confluence domain

 |
| Scopes | 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. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP request |
| Required inputs | 
-   Client key
-   Secret

 |
| Scopes | Allows access to all endpoints in the CoreLogic API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   API Key ID
-   Region
-   Company Name

 |
| Scopes | Allows access to all endpoints in the Cortex XDR API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | For On-Prem Cribl instances:
-   Username
-   Password
-   Server URL

For Cribl.Cloud:

-   Client ID
-   Client Secret
-   Server URL

 |
| Scopes | Determined by the permissions chosen when generating the above inputs. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Criminal IP API |

> **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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | 
-   Client ID
-   Client secret
-   Base URL for API (can be identified in the API client page where the API client is created).

 |
| Scopes | Defined by the user generating the API client. |



> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   API Key
-   Domain

 |
| Scopes | API key role must be defined with granular access |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Secret key

 |
| Scopes | API calls within the confines of the token's roles |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Secret Key
-   Access ID
-   Endpoint URL

 |
| Scopes | Determined by the permissions of the associated user |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 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:  
-   Public token
-   Private token
-   Darktrace domain

 |
| Scopes | Defined by the user generating the API token. |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   The Databricks host, specified as \`https://accounts.cloud.databricks.com\` for account operations or the target [workspace URL](https://docs.databricks.com/aws/en/workspace/workspace-details#workspace-url), for example \`https://dbc-a1b2345c-d6e7.cloud.databricks.com\` for workspace operations.
-   The Databricks account ID, for Databricks account operations.
-   The service principal client ID.
-   The service principal secret.

 |
| Scopes | 

-   Databricks [account-level CLI commands or REST API requests](https://docs.databricks.com/api/account/introduction)  
    OR
-   Databricks [workspace-level CLI commands or REST API requests](https://docs.databricks.com/api/workspace)

And [set permissions](https://docs.databricks.com/aws/en/dev-tools/auth/oauth-m2m?language=Python#step-1-assign-permissions-to-your-service-principal) on your service principal |



Personal access token connect flow

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Personal access token
-   Workspace URL

 |
| Scopes | Defined by the account generating the access token

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   Application Key
-   Site

 |
| Scopes | Determined by the user creating the credential. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   Drata region (NA or EU)The Drata API uses region-specific base URLs. This will save the relevant base URL for your instance.

 |
| Scopes | Defined by the user generating the API client. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Dropbox |
| Scopes | Files and folders scopes:
-   *files.metadata.write*: view and edit information about your Dropbox files and folders
-   *files.metadata.read*: view information about your Dropbox files and folders
-   *files.content.write*: edit content of your Dropbox files and folders
-   *files.content.read*: view content of your Dropbox files and folders

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Depends on the environment: SaaS:
-   Environment ID
-   Token

ActiveGate

-   Domain
-   Environment ID
-   Token

 |
| Scopes | Set at token creation |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Application endpoint URL
    -   Elasticsearch (required)
    -   Kibana (required)
    -   APM (optional)
    -   Fleet (optional)
    -   Enterprise search (optional)

 |
| Scopes | Privileges specified by user creating the API key |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the EmailRep API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   API endpoint

 |
| Scopes | Allows access to all endpoints in the Entro Security API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API Token Domain |
| Scopes | Determined by the permissions of the associated user. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the Feedly API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Figma |
| Scopes | 
-   *files:read*: read files, projects, users, versions, comments, components & styles, and webhooks.
-   *file\_comments:write*: post and delete comments and comment reactions in files.

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | API key |
| Scopes | Permissions are tied to the account the API key is generated for. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

 |
| Scopes | Access varies based on the permissions available to your Freshdesk user profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   Domain

 |
| Scopes | Access depends on the permissions available to your Freshservice user profile |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key (personal access token) |
| Scopes | Defined by the user generating the token. Only API calls with the "scan" scope count toward the quota. |

> **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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | 
-   Client ID
-   Client Secret
-   Scopes

 |
| Scopes | Defined by the user generating the OAuth app. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in GitLab |
| Scopes | 
-   *api*: access the authenticated user's API
-   *read\_api*: read API
-   *read\_user*: read the authenticated user's personal information
-   *read\_repository*: allows read-only access to the repository

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Google Calendar |
| Scopes | *https://www.googleapis.com/auth/calendar*: See, edit, share, and permanently delete all the calendars you can access using Google Calendar. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Google Docs |
| Scopes | *https://www.googleapis.com/auth/documents*: See, edit, create, and delete all your Google Docs documents |



> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request that includes a JWT token request. |
| Required inputs | 
-   [Private key JSON file](https://developers.google.com/workspace/guides/create-credentials#create_credentials_for_a_service_account) of service account
-   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. If you are not accessing a particular user's account, enter the service account email address. You can change this later as needed.
-   [Scopes](https://developers.google.com/identity/protocols/oauth2/scopes): a space separated list of oauth2 scopes enabled for the service account on the [Domain-wide delegation page](https://developers.google.com/workspace/guides/create-credentials#optional_set_up_domain-wide_delegation_for_a_service_account).

 |
| Scopes | Defined by the user managing the service account in your organization. Scopes that can be enabled are listed [here](https://developers.google.com/identity/protocols/oauth2/scopes). |

> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Google Sheets |
| Scopes | *https://www.googleapis.com/auth/spreadsheets*: See, edit, create, and delete all your Google Sheets spreadsheets |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Greenhouse |
| Scopes | 
-   *candidates.create*: Create new candidates or prospects
-   *candidates.view*: View candidates imported via this partner
-   *jobs.view*: View my jobs

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the GreyNoise API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Determined by the token type chosen. See [docs](https://developer.hashicorp.com/terraform/cloud-docs/api-docs#authentication) for further details. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | [Authorization](https://haveibeenpwned.com/API/v3#Authorisation) 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. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Token
-   ID

 |
| Scopes | Access depends on the permissions assigned. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in HubSpot |
| Scopes | 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 [documentation](https://developers.hubspot.com/docs/api/working-with-oauth#scopes).      
-   *oauth:* Basic scope required for OAuth. This scope is added by default to all apps.
-   *forms*: This includes access to the Forms endpoints. Works with Forms endpoints.
-   *tickets*: This includes access to tickets. Works with the Tickets endpoints.
-   *e-commerce*: This includes access to e-commerce features. Works with Products and line items endpoints. Only *Professional* and *Enterprise* accounts can use this scope for the Products API.
-   *sales-email-read*: 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.
-   *forms-uploaded-files*: Download files submitted through a form. Allows users to get a file uploaded via form submission endpoint.
-   *crm.objects.contacts.read*: View properties and other details about contacts. Works with the Contacts endpoints.

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Defined by the user generating the API token. |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID for your API client
-   Client secret for your API client

 |
| Scopes | Determined by the user for their API client in Hyperproof |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Defined by the user generating the API token |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Username
-   Password
-   Base URL
-   WAPI version

 |
| Scopes | Allows access to all endpoints in the Infoblox NIOS API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Intercom |
| Scopes | 
-   *Read and list users and companies*
-   *Write users and companies*
-   *Read and write users*
-   *Read one user and one company*
-   *Read events*
-   *Write events*
-   *Read conversations*
-   *Write conversations*
-   *Read tags*
-   *Write tags*
-   *Read counts*
-   *Export message data*
-   *Export content data*
-   *Read content data*
-   Read admins
-   *Update admins*
-   *Read one admin*
-   *Read admin activity logs*
-   *Read and list articles*
-   *Read and write articles*
-   *Create phone call redirects*
-   *Read and list news items and newsfeeds*
-   *Read and write news items and newsfeeds*

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the IPinfo API depending on plan |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the IPQualityScore API depending on plan |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to different endpoints in the IPstack API depending on IPstack subscription plan |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the IP2Location API depending on subscription. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   API URL

 |
| Scopes | Determined by the user creating the API token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client secret
-   Jamf domain

 |
| Scopes | Allows access to all endpoints in the Jamf Pro API |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API access token |
| Scopes | Allows access to all endpoints in the Jeli API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Jina AI API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Account email associated with the API key
-   Jira domain

 |
| Scopes | 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. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Account email associated with the API key
-   Jira domain

 |
| Scopes | 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. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Joe Sandbox API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the JumpCloud API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Account ID

 |
| Scopes | Scopes are determined by the user creating the API token |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key

 |
| Scopes | Access depends on the type of API key selected and the permissions available to your Klaviyo user profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   App ID
-   App secret

 |
| Scopes | Determined by creator of the app |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Defined by the user generating the token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Mailchimp |
| Scopes | Allows access to all endpoints in the Mailchimp marketing API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | At various steps you will be prompted to enter the following:  
-   Account ID
-   Client ID
-   Client Secret

 |
| Scopes | Determined by the permissions of the associated user |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain



 |
| Scopes | 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. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Application (client) ID
-   Directory (tenant) ID
-   Client secret value
-   Deployment environment (global or US gov)

 |
| Scopes | This connect flow supports *Application* type permissions. With this type of permission, the app will perform actions as itself rather than impersonating your signed-in user. Do not select *delegated* permissions. All permissions are determined by the user managing the application. |



> **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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Application (client) ID
-   Directory (tenant) ID
-   Client secret value
-   Deployment environment

 |
| Scopes | This connect flow supports *Application* type permissions. With this type of permission, the app will perform actions as itself rather than impersonating your signed-in user. Do not select *delegated* permissions. All permissions are determined by the user managing the application. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Microsoft OneDrive |
| Scopes | 
-   *offline\_access*: Maintain access to data you have given it access to
-   *User.Read*: Sign in and read user profile
-   *Files.ReadWrite*: Have full access to user files
-   *Files.ReadWrite.All*: Have full access to all files user can access

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Microsoft Outlook |
| Scopes | 
-   *offline\_access*: Maintain access to data you have given it access to
-   *User.Read*: Sign in and read user profile
-   *Calendars.ReadWrite*: Have full access to user calendars
-   *Calendars.ReadWrite.Shared*: Read and write user and shared calendars
-   *Contacts.ReadWrite*: Have full access to user contacts
-   *Mail.ReadWrite*: Read and write access to user mail
-   *Mail.ReadWrite.Shared*: Read and write user and shared mail
-   *Mail.Send*: Send mail as a user
-   *Mail.Send.Shared*: Send mail on behalf of others

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Microsoft Teams |
| Scopes | 
-   *offline\_access*: Maintain access to data you have given it access to
-   *User.Read*: Sign in and read user profile
-   *Channel.ReadBasic.All*: Read the names and descriptions of channels
-   *ChannelMessage.Read.All*: Read user channel messages
-   *ChannelMessage.Send*: Send channel messages
-   *Chat.ReadWrite*: Read and write user chat messages
-   *Team.ReadBasic.All*: Read the names and descriptions of teams
-   *TeamsAppInstallation.ReadForUser*: Read user's installed Teams apps

 |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client secret

 |
| Scopes | Allows access to all endpoints in the Mimecast 2.0 API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Authentication key |
| Scopes | Allows access to all endpoints in the MISP API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Account domain

 |
| Scopes | Personal tokens mirror all permission levels set in the monday.com UI, including board, column, item or acount access. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   URL

 |
| Scopes | Defined by the user generating the API token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Netlas API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the New Relic API |

> **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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of a credential. At various steps you will be prompted to enter the following:  
-   Account ID
-   Consumer Key
-   Consumer Secret
-   Scopes

 |
| Scopes | Defined by the user generating the Consumer Key and Consumer Secret in the NetSuite product |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client secret
-   Scopes

 |
| Scopes | Defined by the user creating the application. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Notion |
| Scopes | Allows access to all endpoints in the [Notion API](https://developers.notion.com/reference/intro). |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Okta Organization
-   Client ID
-   Public Key
-   Private Key
-   Scopes

 |
| Scopes | Granted in the application. Note: Only a super admin has permission to grant scopes to an app. |



API key connect flow

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Okta API token
-   Okta domain

 |
| Scopes | 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. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

 |
| Scopes | Determined by the permissions of the associated user |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client Secret
-   Subdomain

 |
| Scopes | Determined by the user creating the credential |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Email

 |
| Scopes | Allows access to all endpoints in the Onetime Secret API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

or

-   Access token (if using client credentials)
-   Domain

 |
| Scopes | Determined by user creating the API key or access token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Oomnitza URL

 |
| Scopes | Aligned with the scopes of the account the API key is created within |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | OpenAI Project API key |
| Scopes | Defined by the user generating the API key. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   If your instance is EU based (yes or no input)

 |
| Scopes | Determined by the user creating the API key |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Base URL

 |
| Scopes | Determined by the user generating the API token |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API Key |
| Scopes | Defined by the user generating the API key. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Within your selected service, an integration key for the Events API V2 integration. |
| Scopes | Allows access to all endpoints in the PagerDuty Events API |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   API URL

 |
| Scopes | Defined by user generating the API token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   API Secret

 |
| Scopes | Defined by the user generating the API client and secret |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API Key |
| Scopes | Allows access to all endpoints in the Pinecone API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Token
-   Domain

 |
| Scopes | Access depends on the permissions available to your Pipedrive user profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Productboard |
| Scopes | 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.
-   *custom\_fields:read*: The application will be able to read hierarchy entity custom field definitions.
-   *members\_pii:read*: The application will be able to read personally identifying information (names, email addresses, etc.) from your Productboard users.
-   *notes:create*: The application will be able to create notes.
-   *notes:read*: The application will be able to read notes.
-   *companies:read*: The application will be able to read companies.
-   *plugin\_integrations:manage*: The application will be able to introduce and manage a UI plugin on your Productboard space.
-   *product\_hierarchy\_data:create*: The application will be able to create all features and attributes in your workspace. This includes releases, roadmaps, and features.
-   *product\_hierarchy\_data:manage*: The application will be able to edit and delete features and attributes in your workspace. This includes releases, roadmaps, and features.
-   *product\_hierarchy\_data:read*: 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.
-   *releases:create*: The application will be able to create releases and release groups.
-   *releases:manage*: The application will be able to edit and delete releases and release groups.
-   *releases:read*: The application will be able to read releases and release groups.
-   *users:manage*: The application will be able to edit (note creating) users
-   *users:read*: The application will be able to list users.
-   *users\_pii:read*: The application will be able to read personally identifying information (names, email addresses, etc.) from (note-creating) users.
-   *objectives:read*: The application will be able to read objectives.
-   *objectives:create*: The application will be able to create new objectives.
-   *objectives:manag*e: The application will be able to edit and delete objectives.

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Username
-   Password
-   Server URL

 |
| Scopes | Defined by the administrator API role |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Service principal
-   Secret

 |
| Scopes | Allows access to all endpoints in the Proofpoint TAP API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

 |
| Scopes | Allows access to all endpoints in the Proofpoint Threat Response API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Pulsedive API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key (user key or organizational key)
-   Data region

 |
| Scopes | 

-   User key: inherits generating user's account permissions
-   Organization key: allows access to Insight product APIs and can only be generated by admins

 |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API Key |
| Scopes | Defined by the user requesting the API key. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Token
-   Domain

 |
| Scopes | Allows access to all endpoints in the RegScale API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Rippling API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key

 |
| Scopes | Access depends on the type of API key selected and the permissions available to the associated users Rootly profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Client ID
-   Client Secret
-   Tenant URL
-   Scopes

 |
| Scopes | Defined by the user generating the token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | Environment you would like to connect to (Production or Sandbox). |
| Scopes | 
-   *api*: 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.
-   *id*: Access the identity URL service. Allows access to the identity URL service such as profile, email, address, and phone.
-   *refresh\_token*: 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. 

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Secure Annex API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Defined by the user generating the API token |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

 |
| Scopes | Access depends on the type of API key selected and the permissions available to your SendGrid user profile |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   SentinelOne domain

 |
| Scopes | The API token generated is limited to the RBAC role configuration defined for the token |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Auth token |
| Scopes | Defined by the user generating the token. Scopes are listed [here](https://docs.sentry.io/api/permissions/). |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Username
-   Password
-   ServiceNow domain

 |
| Scopes | Allows access to all endpoints in the ServiceNow API that use basic authentication |

### OAuth

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | 
-   Client ID
-   Client secret
-   Domain

 |
| Scopes | Determined by user creating the app. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Shodan API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Slack |
| Scopes | 
-   *channels:manage*: Manage public channels that Tines has been added to and create new ones
-   *channels:read*: View basic information about public channels in a workspace
-   *chat:write*: Send messages as @Tines
-   *chat:write.public*: Send messages to channels @Tines isn't a member of
-   *files:read*: View files shared in channels and conversations that Tines has been added to
-   *files:write*: Upload, edit, and delete files as Tines
-   *groups:write*: Manage private channels that Tines has been added to and create new ones
-   *mpim:write*: Start group direct messages with people
-   *reactions:read*: View emoji reactions and their associated content in channels and conversations that Tines has been added to
-   *reactions:write*: Add and edit emoji reactions
-   *team:read*: View the name, email domain, and icon for workspaces Tines is connected to
-   *users.profile:read*: View profile details about people in a workspace
-   *users:read*: View people in a workspace
-   *users:read.email*: View email addresses of people in a workspace

 |



### Custom app for Slack

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Slack OAuth Token for your custom app |
| Scopes | Defined by the user creating the custom Slack app. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Organization ID

 |
| Scopes | Allows access to all endpoints in the SlashID API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | 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:
-   Client ID
-   Client secret
-   Snowflake URL
-   Access role for Tines to use to request access to your data

 |
| Scopes | 

-   *session:role:<custom-role>*: Maps to a custom Snowflake role inputted by you in the previous step.
-   *refresh\_token*: Allows authorization server to use short lifetimes for access tokens without needing to involve the user when the token expires.

 |



> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Snyk |
| Scopes | 
-   *org.read*: View Organization information and settings
-   *org.edit*: Edit Organization information and settings
-   *org.report.read*: View reports in your Organization
-   *org.project.create*: Add new Projects
-   *org.project.read*: View Project information and settings and view Organization targets
-   *org.project.edit*: Edit Project information
-   *org.project.delete*: Permanently remove Projects and permanently remove Organization targets
-   *org.project.status*: Activate and deactivate Projects
-   *org.project.test*: Test Projects
-   *org.project.ignore.create*: Create new Project ignores
-   *org.project.ignore.read*: View Project ignore information
-   *org.project.ignore.edit*: Configure Project ignores
-   *org.project.ignore.delete*: Permanently remove Project ignores
-   *org.project.tag.edit*: Create, apply and remove Project tags
-   *org.project.pr.create*: Create fix pull requests for Projects
-   *org.project.pr.skip*: Skip failed security tests on pull requests by marking checks as successful
-   *org.project.jira.issue.read*: View Jira issue information
-   *org.project.jira.issue.create*: Create new Jira issues
-   *org.package.test*: Test packages in ecosystems supported by Snyk

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Username
-   Password
-   Domain

 |
| Scopes | Determined by the user account |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Basic Auth |
| Required inputs | Username, Password and SolarWinds ITSM Domain |
| Scopes | Allows access to all API endpoints that the user account has permission to. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | Access token:
-   Access token
-   Base URL

Basic auth:

-   Username
-   Password
-   Base URL

 |
| Scopes | Access is determined by the user and authorization type selected. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | JSON object with the following:
-   ph-auth-token
-   server

 |
| Scopes | Defined by the user generating the token. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Application (client) ID
-   Client secret value
-   Scopes

 |
| Scopes | Allows access to Spotify's Web API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the Spur API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Token

 |
| Scopes | Access depends on the permissions available to your Stairwell user profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | 
-   Client ID
-   Client secret
-   Scopes

 |
| Scopes | Determined by the user creating the app. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key

 |
| Scopes | Access depends on the permissions available to the associated users Stripe profile. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Sublime Security API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Access ID
-   Access Key
-   Site

 |
| Scopes | Allows access to all endpoints that the account running the key has access to. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token

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\` |
| Scopes | The API token is generated for a user and limited to the permissions associated with that user. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API token
-   Your Sysdig tenant region

 |
| Scopes | Specific to the user and team the token is created within. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the Tavily API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Access Key
-   Secret Key
-   Tenable Security Center domain

 |
| Scopes | 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. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Access Key
-   Secret Key

 |
| Scopes | 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. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   TheHive Project URL of your instance

 |
| Scopes | 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. |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Access ID
-   Secret key

 |
| Scopes | Determined by the system role assigned for the API user |

#### Tines

[Tines](https://www.tines.com/) is a** **platform purpose-built to automate and integrate business processes for the whole team.

## Connect flow details

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   Type of API key to generate: tenant owner, team, or personal
-   Name of API key credential to generate
-   If a team API key is selected, the team to generate the key for

 |
| Scopes | This varies based on the type of key selected:

-   Tenant owner: access to the entire tenant
-   Team: role-based access to a specific team on the tenant
-   Personal: a key tied to your identity and permissions

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to Traceable's public API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Token

 |
| Scopes | Determined by the user generating the token |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API Key
-   Secret
-   Account SID

 |
| Scopes | Determined by the user creating the API key. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Typeform |
| Scopes | 
-   *forms:read*: Retrieve existing forms in your Typeform account. Retrieve customizable messages for your forms
-   *forms:write*: Create, update, and delete your forms
-   *webhooks:read*: Retrieve data about specified Webhooks in your Typeform account
-   webhooks:write: Create, update, and delete specified Webhooks in your Typeform account
-   *responses:read*: Retrieve form responses, landing, and submission information
-   *accounts:read*: Retrieve your name, email, and account language
-   *workspaces:read*: Request data about your workspaces
-   *offline*: Request data on your behalf when you're not online

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the UnpacMe API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the UpGuard API depending on chosen scopes in UI. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | JWT |
| Required inputs | Downloaded JSON file provided by Uptycs. This will include the Customer ID, API key, Secret, and API domain. |
| Scopes | Defined by the permissions set on the user profile the key is generated within. |

#### 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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | The flow will guide you through the creation of an app. At various steps you will be prompted to enter the following:  
-   Client ID
-   Client Secret
-   Org ID
-   Audience

 |
| Scopes | Allows access to all endpoints in the Upwind API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the URLScan.io API |

> **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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client secret
-   Scopes

 |
| Scopes | Determined by the user creating the client ID and secret |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API key |
| Scopes | Allows access to all endpoints in the VirusTotal API |

> **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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | 
-   API key
-   Domain

 |
| Scopes | Allows access to all endpoints in the VMRay Security API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | Text |
| Required inputs | API token |
| Scopes | Allows access to all endpoints in the WiGLE API |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | 
-   Client ID
-   Client Secret
-   Authorization token URL
-   GraphQL endpoint URL

 |
| Scopes | Defined by the user creating the service account. |



> **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

| Item | Description |
| --- | --- |
| Credential type | Managed OAuth |
| Required inputs | N/A: sign in with the account you'd like the credential associated with in Zendesk |
| Scopes | 
-   *read*: gives access to GET endpoints. Includes permission to sideload related resources
-   *write*: gives access to POST, PUT, and DELETE endpoints

 |

#### 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

| Item | Description |
| --- | --- |
| Credential type | HTTP Request |
| Required inputs | **Activated** Server to Server OAuth App
-   Account ID
-   Client ID
-   Client secret

 |
| Scopes | Defined by the user creating the app. |



> **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

| Item | Description |
| --- | --- |
| Credential type | OAuth 2.0 |
| Required inputs | At various steps you will be prompted to enter the following:  
-   REST root URL
-   Client ID
-   Client Secret

 |
| Scopes | Determined by the permissions of the associated user |

## 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, priorities 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
- For each case priority, view its name

```
INFO.cases.statuses.sub_status_name.id
INFO.cases.case_field_input_name.id
INFO.cases.priorities.priority_name.name
```

### 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 |  |
| `team_case` | Access details of the current [case](https://www.tines.com/docs/cases/) |  |

### Functions

#### ABS

Returns the absolute value of a number.

**Categories:** Numbers

## Syntax

```
ABS(number)
```

## 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

Computes the arc cosine of x (in radians), returning a number between 0 and π

**Categories:** Numbers

## Syntax

```
ACOS(x)
```

#### AES_DECRYPT

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

## Syntax

```
AES_DECRYPT(encoded_text, key, [iv])
```

## 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

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

## Syntax

```
AES_ENCRYPT(text, key, [iv], [mode: "aead"], [expires_in])
```

## 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

Returns `TRUE` if all arguments are truthy, otherwise returns `FALSE`.

**Categories:** Logic

## Syntax

```
AND(value1, value2, ...)
```

## 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

Joins two or more pieces of text together.

**Categories:** Text

## Syntax

```
APPEND(text1, text2, ...)
```

## 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

Returns an Array with each of the arguments as a member.

**Categories:** Arrays

## Syntax

```
ARRAY(element1, element2, ...)
```

## 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

Computes the arc sine of x (in radians), returning a number between -π/2 and π/2

**Categories:** Numbers

## Syntax

```
ASIN(x)
```

#### AT_LEAST

Limits a number to a minimum value.

**Categories:** Numbers

## Syntax

```
AT_LEAST(number, minimum)
```

## 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

Limits a number to a maximum value.

**Categories:** Numbers

## Syntax

```
AT_MOST(number, maximum)
```

## 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

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

## Syntax

```
AVERAGE(array) or AVERAGE(number1, number2, ...)
```

## 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

Decode URL-safe Base64 encoded text.

**Categories:** Data Parsing/Conversion

## Syntax

```
BASE64URL_DECODE(text)
```

## 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

Encode text to URL-safe Base64 encoded text.

**Categories:** Data Parsing/Conversion

## Syntax

```
BASE64URL_ENCODE(text)
```

## 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

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

## Syntax

```
BASE64_DECODE(text, [strict: TRUE])
```

## 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

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

## Syntax

```
BASE64_ENCODE(text_or_object, [strict: TRUE])
```

## 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

Counts the number of bytes in a string.

**Categories:** Text

## Syntax

```
BYTESIZE(string)
```

## 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

Converts the input to camel case.

**Categories:** Text

## Syntax

```
CAMELIZE(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

Makes the first character of text uppercase and converts the remaining characters to lowercase.

**Categories:** Text

## Syntax

```
CAPITALIZE(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

Rounds the input up to the nearest whole number.

**Categories:** Numbers

## Syntax

```
CEIL(number)
```

## 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

Chunks an array into arrays with chunk_size elements. The last chunk may contain less than chunk_size elements.

**Categories:** Arrays

## Syntax

```
CHUNK_ARRAY(array, chunk_size)
```

## 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

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

## Syntax

```
CLAMP(input, min, max)
```

## 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

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

## Syntax

```
COMBINE_COLUMNS_AND_ROWS(columns, rows)
```

## 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

Removes any `null` values from an array.

**Categories:** Arrays

## Syntax

```
COMPACT(array)
```

## 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

Concatenates (joins together) two or more arrays.

**Categories:** Arrays

## Syntax

```
CONCAT(array1, array2, ...)
```

## 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

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

## Syntax

```
CONST_EQUALS(string1, string2)
```

## 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

Checks if a text value contains a URL. This function returns true if any part of the string matches a URL pattern.

**Categories:** Text

## Syntax

```
CONTAINS_URL(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

Computes the cosine of x (in radians), returning a number between -1 and 1

**Categories:** Numbers

## Syntax

```
COS(x)
```

#### COUNTIF

Returns the count of all elements in an array that are equal to the comparison argument.

**Categories:** Arrays, Lambdas

## Syntax

```
COUNTIF(array, comparison)
```

## 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

Parses CSV-formatted text with auto-detection of the delimiter character and parses output into an array of arrays.

**Categories:** Data Parsing/Conversion

## Syntax

```
CSV_PARSE(text)
```

## 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

Parses CSV-formatted text with auto-detection of the delimiter character and parses output into an array of objects.

**Categories:** Data Parsing/Conversion

## Syntax

```
CSV_PARSE_TO_OBJECTS(text)
```

## 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

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

## Syntax

```
DATE(date, format, timezone)
```

## 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

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

## Syntax

```
DATE_DIFF(start_time, end_time, [absolute: TRUE])
```

## 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

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

## Syntax

```
DATE_PARSE(date, format, timezone)
```

## 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

Returns the day of the month for the specified date. Accepted Format: year-month-day (year/month/day)

**Categories:** Dates/Times

## Syntax

```
DAY(date)
```

## 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

Creates a new object by recursively merging two or more objects together. Where there are key collisions that last value is used.

**Categories:** Objects

## Syntax

```
DEEP_MERGE(object, object, ...)
```

## 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

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. When every argument is empty, the first fallback is returned by default. Pass `{use_last_fallback: TRUE}` as the final argument to return the last fallback instead.

**Categories:** Logic, Arrays, Text

## Syntax

```
DEFAULT(value, fallback1, fallback2, ..., [{use_last_fallback: FALSE}])
```

## 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"
```

### Example 8

Input:

```json
{
  "my_action": {
    "message": [],
    "first_ref": null,
    "second_ref": []
  }
}
```

Formula:

```
DEFAULT(my_action.message, my_action.first_ref, my_action.second_ref)
```

Output:

```json
null
```

### Example 9

Input:

```json
{
  "my_action": {
    "message": [],
    "first_ref": null,
    "second_ref": []
  }
}
```

Formula:

```
DEFAULT(my_action.message, my_action.first_ref, my_action.second_ref, {use_last_fallback: TRUE})
```

Output:

```json
[]
```

## 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

Deletes an item from an array at a given index and returns the array minus that deleted value.

**Categories:** Arrays

## Syntax

```
DELETE(array, index)
```

## 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

Returns an array of items that appear in the first array but not in the second array

**Categories:** Arrays

## Syntax

```
DIFFERENCE(array1, array2)
```

## 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

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

## Syntax

```
DISTANCE_OF_TIME_IN_WORDS(date, [from])
```

## 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

Divides a number by the specified number.

**Categories:** Numbers

## Syntax

```
DIVIDED_BY(number, denominator)
```

## 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

Makes each character in text lowercase.

**Categories:** Text

## Syntax

```
DOWNCASE(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

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

## Syntax

```
EML_PARSE(text, [single_body: TRUE])
```

## 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

Returns true if the text ends with the suffix

**Categories:** Text

## Syntax

```
ENDS_WITH(text, suffix)
```

## 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

Raises an error with the specified message.

**Categories:** Logic

## Syntax

```
ERROR(message)
```

## 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

Escapes text by replacing characters with escape sequences (e.g. so that the text can be used in a URL)

**Categories:** Text

## Syntax

```
ESCAPE(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

Escapes text without changing existing escaped entities.

**Categories:** Text

## Syntax

```
ESCAPE_ONCE(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

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

## Syntax

```
ESTIMATED_TOKEN_COUNT(input)
```

## 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

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

## Syntax

```
FILTER(array | object, values_to_keep | LAMBDA(arg1, [arg2], expr))
```

## 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

Returns the first element in the array for which the lambda returns true.

**Categories:** Lambdas, Arrays

## Syntax

```
FIND(array, LAMBDA(item, [expr]))
```

## 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

Returns the first element of an array.

**Categories:** Arrays

## Syntax

```
FIRST(array)
```

## 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

Turn nested arrays into a single 1 dimensional array

**Categories:** Arrays

## Syntax

```
FLATTEN(array)
```

## 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

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

## Syntax

```
FLATTEN_JSON(object, [separator])
```

## 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

Rounds a number down to the nearest whole number.

**Categories:** Numbers

## Syntax

```
FLOOR(number)
```

## 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

If the supplied argument is an array it returns it, otherwise wraps it in an array

**Categories:** Text

## Syntax

```
FORCE_ARRAY(anything)
```

## 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

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

## Syntax

```
GENERATE_RSA_KEYS()
```

## 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 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

## Syntax

```
GET(object, path)
```

## 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

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

## Syntax

```
GROUP_BY(items, key_or_lambda, [as_array: FALSE])
```

## 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

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

## Syntax

```
GUNZIP(compressed_file_data)
```

#### GZIP

Compresses a single file with gzip

**Categories:** Data Parsing/Conversion

## Syntax

```
GZIP(file_contents)
```

#### HCL_PARSE

Parses Hashicorp Configuration Language (HCL) text

**Categories:** Data Parsing/Conversion

## Syntax

```
HCL_PARSE(hcl_text)
```

#### HEX_PARSE

Converts hexadecimal text into the text or data it represents

**Categories:** Data Parsing/Conversion

## Syntax

```
HEX_PARSE(hex_text, [error_on_invalid_hex: FALSE])
```

## 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

Converts text into a SHA-1 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Syntax

```
HMAC_SHA1(text, secret)
```

## 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

Converts text into a Base64 encoded SHA-1 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Syntax

```
HMAC_SHA1_BASE64(text, secret)
```

## 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

Converts text into a SHA-256 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Syntax

```
HMAC_SHA256(text, secret)
```

## 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

Converts text into a Base64 encoded SHA-256 hash using a hash message authentication code (HMAC).

**Categories:** Hashing/Signing

## Syntax

```
HMAC_SHA256_BASE64(text, secret)
```

## 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

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

## Syntax

```
HOUR(date)
```

## 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

Converts escaped characters in text to HTML syntax characters, e.g. `&lt;élan&gt;` becomes `<élan>`.

**Categories:** Text

## Syntax

```
HTML_DECODE(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

Escapes HTML syntax characters in text, e.g. `<élan>` becomes `&lt;élan&gt;`.

**Categories:** Text

## Syntax

```
HTML_ENCODE(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` is `TRUE` returns second argument, if `FALSE` returns `null` or the third argument.

**Categories:** Logic

## Syntax

```
IF(condition, value_if_true, [value_if_false])
```

## 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 `expression` produces an error, the result of `fallback_expression` is returned. If there is no error, the result of `expression` is returned.

**Categories:** Logic

## Syntax

```
IF_ERROR(expression, fallback_expression)
```

## 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

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

## Syntax

```
INCLUDES(target, value)
```

## 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

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

## Syntax

```
INDEX_OF(string_or_array, object | LAMBDA(value, expr))
```

## 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

Inserts an item into an array at a given index.

**Categories:** Arrays

## Syntax

```
INSERT(array, index, item)
```

## 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

Returns the intersection between two arrays.

**Categories:** Arrays

## Syntax

```
INTERSECTION(array1, array2)
```

## 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

Checks if an IP address or a list of IP addresses are in a given CIDR block.

**Categories:** IP Addresses

## Syntax

```
IN_CIDR(ip_address, cidr_range)
```

## 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

Returns true if a value is falsy ie. `FALSE`, `""`, `[]`, `{}`, `NULL`, and false otherwise. [`IS_PRESENT`](https://www.tines.com/docs/formulas/functions/is-present/) is the opposite of this. See also [`IS_EMPTY`](https://www.tines.com/docs/formulas/functions/is-empty).

**Categories:** Text, Objects, Arrays, Logic, Numbers

## Syntax

```
IS_BLANK(any)
```

## 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

Checks if a text value follows the email standard. This does not guarantee that it's a valid email.

**Categories:** Text

## Syntax

```
IS_EMAIL(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

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

## Syntax

```
IS_EMPTY(text | array | object)
```

## 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

Checks if a text value is a valid IPV4 address.

**Categories:** IP Addresses, Text

## Syntax

```
IS_IPV4(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

Checks if a text value is a valid IPV6 address.

**Categories:** IP Addresses, Text

## Syntax

```
IS_IPV6(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

Checks if a text value is a valid IP address

**Categories:** IP Addresses

## Syntax

```
IS_IP_ADDRESS(text)
```

## 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

Checks if a text value is valid JSON.

**Categories:** Text

## Syntax

```
IS_JSON(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

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

## Syntax

```
IS_PRESENT(any)
```

## 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

Checks if a text value follows the URL standard. This does not guarantee that it's a valid URL.

**Categories:** Text

## Syntax

```
IS_URL(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

Returns true if the object provided matches the provided JSON schema, using the [JSON Schema syntax](https://json-schema.org/).

**Categories:** Objects

## Syntax

```
IS_VALID_JSON_SCHEMA(object, any)
```

## 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

Checks if a text value is valid XML.

**Categories:** Text

## Syntax

```
IS_XML(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

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

## Syntax

```
IS_YAML(text, [parse_date_or_time: FALSE], [strict: FALSE])
```

## 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

Combines the elements in an array into a single text value using the argument as a separator.

**Categories:** Arrays

## Syntax

```
JOIN(array, join_text)
```

## 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

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

## Syntax

```
JSONPATH(object, path)
```

## 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

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

## Syntax

```
JSON_PARSE(text)
```

## 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

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

## Syntax

```
JSON_SCHEMA_VALIDATE(object, any)
```

## 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

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

## Syntax

```
JWK_TO_PEM(jwk, [include_private: FALSE])
```

## 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

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

## Syntax

```
JWT_DECODE(token, secret_or_key, algorithm, **options)
```

## 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

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

## Syntax

```
JWT_SIGN(claim_set, key, [algorithm=RS256], [headers])
```

## 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

Returns an array of keys present in the object.

**Categories:** Objects

## Syntax

```
KEYS(object)
```

## 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

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

## Syntax

```
LAMBDA([parameter1, parameter2, …,] calculation)
```

## 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

Returns the last element of an array.

**Categories:** Arrays

## Syntax

```
LAST(array)
```

## 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

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

## Syntax

```
LAST_INDEX_OF(string_or_array, term)
```

## 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

Parse text from a LDAP Data Interchange Format (LDIF) file into an object

**Categories:** Data Parsing/Conversion

## Syntax

```
LDIF_PARSE(ldif_file_text)
```

## 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

Converts an object parameter to a legacy String representation.

**Categories:** Text

## Syntax

```
LEGACY_STRINGIFY(object)
```

## Examples

### Example 1

Input:

```json
{
  "my_action": {
    "data": {
      "foo": "bar"
    }
  }
}
```

Formula:

```
LEGACY_STRINGIFY(my_action.data)
```

Output:

```json
"{\"foo\"=>\"bar\"}"
```

#### LEVENSHTEIN_DISTANCE

Compute the Levenshtein distance between two strings.

**Categories:** Text

## Syntax

```
LEVENSHTEIN_DISTANCE(string_one, string_two, [insertion_cost: 2], [deletion_cost: 2], [substitution_cost: 1])
```

## 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

Removes all whitespace (tabs, spaces, and newlines) from the beginning of text.

Does not affect spaces between words.

**Categories:** Text

## Syntax

```
LSTRIP(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

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

## Syntax

```
MAP(array_of_objects, path)
```

## 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

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

## Syntax

```
MAP_LAMBDA(array_or_object, lambda)
```

## 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

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

## Syntax

```
MATCH(text, regex_to_match,  [modifiers])
```

## 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

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

## Syntax

```
MAX(array) or MAX(number1, number2, ...)
```

## 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

Calculates the hex encoded MD5 hash of some text.

**Categories:** Hashing/Signing

## Syntax

```
MD5(text)
```

## 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

Calculates the Base64 encoded MD5 hash of text.

**Categories:** Hashing/Signing

## Syntax

```
MD5_BASE64(text)
```

## 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

Returns the median numeric value. Can either be called with a single argument which must be an array or multiple numeric arguments.

**Categories:** Numbers

## Syntax

```
MEDIAN(array) or MEDIAN(number1, number2, ...)
```

## 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

Creates a new object by merging two or more objects together.

**Categories:** Objects

## Syntax

```
MERGE(object, object, ...)
```

## 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

Decodes email header fields encoded in accordance with RFC 2047.

**Categories:** Data Parsing/Conversion

## Syntax

```
MIME_HEADER_DECODE(encoded_string, target_encoding = "utf-8")
```

## 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

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

## Syntax

```
MIN(array) or MIN(number1, number2, ...)
```

## 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

Subtracts a number from another number.

**Categories:** Numbers

## Syntax

```
MINUS(number, number)
```

## 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

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

## Syntax

```
MINUTE(date)
```

## 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

Returns the remainder of a division operation.

**Categories:** Numbers

## Syntax

```
MODULO(numerator, denominator)
```

## 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

Returns the month of the year for the specified date. Accepted Format: year-month-day (year/month/day)

**Categories:** Dates/Times

## Syntax

```
MONTH(date)
```

## 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

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

## Syntax

```
MSG_PARSE(file, [extract_multiline_headers: FALSE], [single_body: TRUE])
```

## 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

Formats and "pretty prints" an object in JSON.

**Categories:** Data Parsing/Conversion

## Syntax

```
NEAT_JSON(object)
```

## 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

Replaces every newline (\n) with an HTML line break (&lt;br&gt;).

**Categories:** Text

## Syntax

```
NEWLINE_TO_BR(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

Returns the logical opposite of the supplied param.

**Categories:** Logic

## Syntax

```
NOT(any)
```

## 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

Returns the current date and time in UTC.

**Categories:** Dates/Times

## Syntax

```
NOW()
```

## 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

Converts the passed value to a number.

**Categories:** Numbers

## Syntax

```
NUMBER(value)
```

## 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

Returns an object with keys and values as specified.

**Categories:** Objects

## Syntax

```
OBJECT(key1, value1, key2, value2, ...)
```

## 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

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

## Syntax

```
OBJECTS_TO_CSV(objects, headers)
```

## 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

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

## Syntax

```
OBJECTS_TO_MARKDOWN_TABLE(objects, headers)
```

## 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

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

## Syntax

```
OPENSSL_DECRYPT(ciphertext, algorithm, key, iv, [aad], [auth_tag])
```

## 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

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

## Syntax

```
OPENSSL_ENCRYPT(text, algorithm, key, [aad])
```

## 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

Returns true if any of the arguments are truthy, otherwise returns false.

**Categories:** Logic

## Syntax

```
OR(value1, value2, ...)
```

## 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

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

## Syntax

```
PAD_LEFT(input, target_length, pad_value)
```

## 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

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

## Syntax

```
PAD_RIGHT(input, target_length, pad_value)
```

## 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

Returns an object that representing the parsed url

**Categories:** Data Parsing/Conversion

## Syntax

```
PARSE_URL(text, [include_public_suffix_domains: FALSE])
```

## 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

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

## Syntax

```
PEM_TO_JWK(pem, [include_private: FALSE])
```

## 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

Outputs the singular or plural version of text based on the value of a number.

**Categories:** Text

## Syntax

```
PLURALIZE(counter, text, [plural_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

Adds a number to another number.

**Categories:** Numbers

## Syntax

```
PLUS(number, number)
```

## 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

Raises a number to the power of another number.

**Categories:** Numbers

## Syntax

```
POWER(base, exponent)
```

## 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

Adds the specified text to the beginning of the other text.

**Categories:** Text

## Syntax

```
PREPEND(text, prefix)
```

## 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

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

## Syntax

```
PROMPT("text")
```

## 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

Adds one or more items to the end of an array.

**Categories:** Arrays

## Syntax

```
PUSH(array, item1, ...)
```

## 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

Returns a random number in the range specified.

**Categories:** Numbers

## Syntax

```
RANDOM(min, max)
```

## 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

Select a random element from an array.

**Categories:** Arrays

## Syntax

```
RANDOM_ELEMENT(array)
```

## 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

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

## Syntax

```
RANDOM_STRING(length, mode, custom_characters)
```

## 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

Returns an array of numbers with the first value equal to `start` and the last value equal to `end`.

**Categories:** Arrays

## Syntax

```
RANGE(start, end)
```

## 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

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

## Syntax

```
REDUCE(array, LAMBDA(previous_value, current_value, [expr]), initial_value)
```

## 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

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

## Syntax

```
REGEX_EXTRACT(text, regex, [modifiers])
```

## 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

Replaces every occurrence of a regex match in text with the second argument.

**Categories:** Text

## Syntax

```
REGEX_REPLACE(text, regex, replacement)
```

## 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

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

## Syntax

```
REJECT(array | object, values_to_remove | LAMBDA(arg1, [arg2], expr))
```

## 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

Removes every occurrence of the specified sub-text from text.

**Categories:** Text

## Syntax

```
REMOVE(text, text_to_remove)
```

## 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

Removes the first occurrence of the specified sub-text from text.

**Categories:** Text

## Syntax

```
REMOVE_FIRST(text, text_to_remove)
```

## 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

Removes the specified key from an object. Path is a dot separated path to the object, you can escape dots using backslash

**Categories:** Objects

## Syntax

```
REMOVE_KEY(object, path)
```

## 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

Replaces every occurrence of the search text with the replacement text.

**Categories:** Text

## Syntax

```
REPLACE(text, search, replace_with)
```

## 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

Replaces the first occurrence of the search text with the replacement text.

**Categories:** Text

## Syntax

```
REPLACE_FIRST(text, search, replace_with)
```

## 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

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

## Syntax

```
REVERSE(array)
```

## 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

Rotates the elements in an array by any number of steps.

**Categories:** Arrays

## Syntax

```
ROTATE(array, steps)
```

## 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

Rounds an input number to the nearest whole number or, if precision is specified, to that number of decimal places.

**Categories:** Numbers

## Syntax

```
ROUND(number, [precision])
```

## 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

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

## Syntax

```
RSA_AES_HYBRID_DECRYPT(encrypted_data_array, key, [padding: "PKCS1_PADDING"])
```

## 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

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

## Syntax

```
RSA_AES_HYBRID_ENCRYPT(plain_message, key, [padding: "PKCS1_PADDING"])
```

## 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

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

## Syntax

```
RSA_DECRYPT(encrypted_message, key, [padding: "PKCS1_PADDING"])
```

## 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

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

## Syntax

```
RSA_ENCRYPT(plain_message, key, [padding: "PKCS1_PADDING"])
```

## 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

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

## Syntax

```
RSA_SIGN(data, key, [digest: "SHA256"])
```

## Examples

### Example 1

Formula:

```
RSA_SIGN(data, key, digest: "SHA256")
```

Output:

```json
"Signed data"
```

#### RSTRIP

Removes all whitespace (tabs, spaces, and newlines) from the right side of text.

**Categories:** Text

## Syntax

```
RSTRIP(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

Returns the second of the minute for the specified date.

**Categories:** Dates/Times

## Syntax

```
SECOND(date)
```

## 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

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

## Syntax

```
SET_KEY(object, path, value)
```

## 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

Calculates the sha1 hash of text.

**Categories:** Hashing/Signing

## Syntax

```
SHA1(text)
```

## 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

Calculates the sha256 hash of text, expressed in hex.

**Categories:** Hashing/Signing

## Syntax

```
SHA256(text)
```

## 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

Calculates the sha256 hash of the text, expressed in base64.

**Categories:** Hashing/Signing

## Syntax

```
SHA256_BASE64(text)
```

## 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

Calculates the sha512 hash of text.

**Categories:** Hashing/Signing

## Syntax

```
SHA512(text)
```

## 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

Shuffles all the elements in an array

**Categories:** Arrays

## Syntax

```
SHUFFLE(array)
```

## 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

Computes the sine of x (in radians), returning a number between -π/2 and π/2

**Categories:** Numbers

## Syntax

```
SIN(x)
```

#### SIZE

Returns the number of characters in text or the number of elements in an array.

**Categories:** Text, Arrays

## Syntax

```
SIZE(text_or_array)
```

## 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

Returns 1 or `length` characters from a piece of text, beginning at the `start_index`.

**Categories:** Text

## Syntax

```
SLICE(text, start_index, [length])
```

## 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

Returns the remainder of the array (or `length` elements of the array, if specified), beginning at the `start_index`.

**Categories:** Arrays

## Syntax

```
SLICE_ARRAY(array, start_index, [length])
```

## 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

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

## Syntax

```
SORT(array, path, [include_json_paths: FALSE])
```

## 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

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

## Syntax

```
SORT_NATURAL(array, path, [include_json_paths: FALSE])
```

## 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

Divides the input text into an array using the delimiter as a separator

**Categories:** Text

## Syntax

```
SPLIT(text, delimiter)
```

## 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

Computes the square root of a given number

**Categories:** Numbers

## Syntax

```
SQRT(x)
```

#### STARTS_WITH

Returns true if the text begins with the prefix

**Categories:** Text

## Syntax

```
STARTS_WITH(text, prefix)
```

## 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

Returns the GUID of the currently executing story run.

**Categories:** Story Metadata

## Syntax

```
STORY_RUN_GUID()
```

## 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

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

## Syntax

```
STORY_RUN_LINK([mode: "story_runs"])
```

## 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

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

## Syntax

```
STRIP(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

Removes any HTML tags from text.

**Categories:** Text

## Syntax

```
STRIP_HTML(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

Removes any newline characters (line breaks) from text.

**Categories:** Text

## Syntax

```
STRIP_NEWLINES(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

Sums the elements in an array of numbers.

**Categories:** Arrays, Numbers

## Syntax

```
SUM(array)
```

## 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

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

## Syntax

```
SWITCH(expression, value_to_compare, value_to_return, ..., default_value)
```

## 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

Counts the occurrences of each unique element within an array, accounting for case sensitivity.

**Categories:** Arrays

## Syntax

```
TALLY(array, [strict_types: FALSE])
```

## 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

Creates a tarball for a list of files

**Categories:** Data Parsing/Conversion

## Syntax

```
TAR(compressed_file_data)
```

## Examples

### Example 1

Input:

```json
{
  "zip_files": [
    {
      "name": "example.txt",
      "contents": "hello, world!"
    },
    {
      "name": "other.csv",
      "contents": "1,2,3"
    }
  ]
}
```

Formula:

```
TAR(files)
```

#### TEXT

Converts the passed value to text.

**Categories:** Text

## Syntax

```
TEXT(value)
```

## 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

Multiplies a number by another number.

**Categories:** Numbers

## Syntax

```
TIMES(number, number)
```

## 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

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

## Syntax

```
TINES_DECRYPT(encrypted, key)
```

## 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

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

## Syntax

```
TINES_ENCRYPT(text, key, [expires_in])
```

## 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

Capitalizes the first letter of each word in a string and replaces some characters in the string to make the title prettier. By default, strings ending in `_id` have that suffix removed. Pass `keep_id_suffix: TRUE` to preserve it.

**Categories:** Text

## Syntax

```
TITLEIZE(text, [keep_id_suffix: FALSE])
```

## 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"
```

### Example 4

Input:

```json
{
  "my_action": {
    "message": "author_id"
  }
}
```

Formula:

```
TITLEIZE(my_action.message, keep_id_suffix: TRUE)
```

Output:

```json
"Author Id"
```

#### TODAY

Returns the current date

**Categories:** Dates/Times

## Syntax

```
TODAY()
```

## 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

Convert an array of arrays into CSV-formatted text.

**Categories:** Data Parsing/Conversion

## Syntax

```
TO_CSV(array_of_arrays)
```

## 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

Converts text or data into hexadecimal text.

**Categories:** Data Parsing/Conversion

## Syntax

```
TO_HEX(text)
```

## 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

Convert an object into JSON text.

**Categories:** Data Parsing/Conversion

## Syntax

```
TO_JSON(object)
```

## 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

Turn input text into snake case. When from_camel_case is true, the function will convert camel case to snake case.

**Categories:** Text

## Syntax

```
TO_SNAKE_CASE(text, [from_camel_case: false])
```

## 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

Converts an object into XML, root element name is optional.

**Categories:** Data Parsing/Conversion

## Syntax

```
TO_XML(object, root_element_name = "hash")
```

## 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

Converts an object into YAML

**Categories:** Data Parsing/Conversion

## Syntax

```
TO_YAML(object)
```

## 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

Replaces non-ASCII characters with an ASCII approximation, or if none exists, a replacement character: “?”.

**Categories:** Text

## Syntax

```
TRANSLITERATE(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

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

## Syntax

```
TRUNCATE(text, length, [ellipsis])
```

## 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

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

## Syntax

```
TRUNCATEWORDS(text, length, [ellipsis])
```

## 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

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

## Syntax

```
TYPE(value)
```

## 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

Transforms a single layer of key/value pairs into a nested JSON structure. Default key separator is a period or full stop (`.`)

**Categories:** Objects

## Syntax

```
UNFLATTEN_JSON(object, [separator])
```

## 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

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

## Syntax

```
UNION(array1, array2)
```

## 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

Removes any duplicate elements in an array.

**Categories:** Arrays

## Syntax

```
UNIQ(array)
```

## 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

Returns the current unix timestamp, i.e. the number of seconds since midnight on 1 January 1970

**Categories:** Dates/Times

## Syntax

```
UNIX_TIMESTAMP()
```

## 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

Extracts a list of files from a tarball.

**Categories:** Data Parsing/Conversion

## Syntax

```
UNTAR(tar_data)
```

#### UNZIP

Extracts files from a ZIP archive.

**Categories:** Data Parsing/Conversion

## Syntax

```
UNZIP(zip_data, [password])
```

## 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

Makes each character in text uppercase.

**Categories:** Text

## Syntax

```
UPCASE(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

Decodes text that has been encoded as a URL or by `URL_ENCODE`.

**Categories:** Text

## Syntax

```
URL_DECODE(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

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

## Syntax

```
URL_ENCODE(text, [encoding: "form"])
```

## 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

Generates a universally unique identifier (UUID)

**Categories:** Text

## Syntax

```
UUID()
```

## 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

Returns all the values of an object.

**Categories:** Objects

## Syntax

```
VALUES(object)
```

## 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

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

## Syntax

```
WHERE(array, path, [value], [null_on_path_error: FALSE])
```

## 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

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

## Syntax

```
WORKBENCH_LINK()
```

#### XML_PARSE

Takes text containing XML and parses to an object

**Categories:** Data Parsing/Conversion

## Syntax

```
XML_PARSE(text)
```

## 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

Takes text containing YAML and parses to an object

**Categories:** Data Parsing/Conversion

## Syntax

```
YAML_PARSE(text, [parse_date_or_time: FALSE], [version: 1])
```

## 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

Returns the year for the specified date.

**Categories:** Dates/Times

## Syntax

```
YEAR(date)
```

## 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

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

## Syntax

```
ZIP(fileContentsOrFilesArray, [filename], [password])
```

## 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

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

## Syntax

```
ZSCALER_OBFUSCATE_API_KEY(api_key, now)
```

## 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.

[Video](https://stream.mux.com/500Hj9ZZ3gqlZ9MY6dVGJD8BNnTrAT6oH/high.mp4)

## 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.

[Video](https://stream.mux.com/d6IDqXabFQXAnEk1m8900C5Oyms01xrWm2/high.mp4)

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 page 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

[Video](https://stream.mux.com/EmTrxOQRzJcD9wz1ZxYRvMwUwgailF01r/high.mp4)

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 (Note: dynamic default values will override any value provided through query parameters). 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.

[Video](https://stream.mux.com/1rYti1UdtT2K64DrQoB5zffOhAY02aj5p/high.mp4)

> **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/).

[Video](https://stream.mux.com/iTQY01x01011hFDYNf1xJPkaz9iaaFrboWn/high.mp4)

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**.

[Video](https://stream.mux.com/DjdponEFVMLEA7CwhQc1AzIB77MTZCqy/high.mp4)

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](https://stream.mux.com/tZA00zrsY8Ipft01Lwl8BUVOCIDGXptXJO/high.mp4)

### 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](https://stream.mux.com/ITPPK3vV618lvNr02C3Ho00Jku4YfkIZXM/high.mp4)

### 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](https://stream.mux.com/j017QqJxPrH444MWDlObtMDbBtXK9Zu1q/high.mp4)

### 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](https://stream.mux.com/tCPHeFduIcbx4lqtFRPsUJViu8JvC9DW/high.mp4)

### 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:

[Video](https://stream.mux.com/emgPxhQmxYJFqnXjWfgT1MSQ02EjNMcDk/high.mp4)

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)

Note: Tenant owners can always access all pages on their tenant. 

### 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.

[Video](https://stream.mux.com/UlmSfDv00UHz7qkHSLMcmdrCkfU005XJ00i/high.mp4)

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 55 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.

[Video](https://stream.mux.com/7Gwqup1Sc3IaTTZHol1sq3dcYlwCDm4A/high.mp4)

### 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.   

[Video](https://stream.mux.com/V7yecGLm02UY54KP7I35Jc01wNaHltEsl2/high.mp4)

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.

[Video](https://stream.mux.com/E01MET9IT01SVhQ02SkdjeTDa6RicDh00DSz/high.mp4)

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. 

[Video](https://stream.mux.com/7101VVo5ODOy5BjBRwNW7KLUcmCT9ynYj/high.mp4)

### 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.

[Video](https://stream.mux.com/a82ypCLIH00Ls4JyVSHkQffmjsfG6JgwE/high.mp4)

### 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. 

[Video](https://stream.mux.com/aO30201RGrXsZKamFNgGHw82rAWbM00li3G/high.mp4)

### 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. 

[Video](https://stream.mux.com/00FEP41kDa7j89jROL19H4DxXI49ITFp2/high.mp4)

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. 

[Video](https://stream.mux.com/GhkmjTFwuiTEiz00xM01YI02hDglIiWkpCq/high.mp4)

#### 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.

[Video](https://stream.mux.com/STlKTElPjDxm2Z3OKY01bIWsSbyvFNAl5/high.mp4)

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. 

[Video](https://stream.mux.com/1lZMowTYebH8eKzwC5oUKTJj2T1XXjOs/high.mp4)

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.`

## Apps

> **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

Apps are a customizable, interactive front end for your Tines workflows.

- Apps enable you to build custom applications in Tines in minutes. Powered by [Workbench](https://www.tines.com/docs/workbench/), Apps use natural language prompts to generate and refine full [React](https://react.dev/) applications.
- Apps can be powered by a single story or multiple story endpoints and integrate directly with your existing Tines workflows, data, and automations.
- An app consists of **UI surfaces**, a **React codebase,** and one or more **app endpoints.** While Apps support prompt-driven development, you retain full control over the underlying React code, styling, and package dependencies, allowing you to customize and extend your App as needed.

---

## Getting started with Apps

Apps live in the main navigation in Tines. From here, you can view a list of previously created Apps or click **New** to start building a new App.

---

## Learning more about Apps

Continue reading to learn more about Apps:

1. [Building Apps](https://www.tines.com/docs/creating-apps/)
2. [Publishing and versioning in Apps](https://www.tines.com/docs/publishing-and-versioning-in-apps/)
3. [Admin settings for Apps](https://www.tines.com/docs/admin-settings-for-apps/)

### Building Apps

Apps are launched from their own surface in Tines. Upon first visit, you'll be welcomed by a few slides to help get you started with Apps. Click the **New **button to create a new App.

![Entry page for Apps, first page seen upon navigating to Apps section](https://www.datocms-assets.com/55802/1780948163-apps_welcome.png)

### The App builder

Once a new App is created and you are in the builder, the build process takes place in the left-hand conversation panel. There are two modes that can be used and changed mid-conversation:

- **Plan:** in plan mode, the App builder is unable to change the code or endpoints. Use plan mode to think through your build before making any changes. These plans are saved as markdown plan documents and are viewable in the **Code** tab.
- **Build:** in build mode, the App builder can make changes to the code and endpoints. It can also read and build plans that were generated in plan mode.



In your first message, describe what you want to build and an initial version of your App will be generated within minutes.

> **TIP:**
> -   Try starting with your design prompting first before moving on to the more complex, endpoints generation side of things.
> -   Use a new chat for each new concept you introduce. There is a 200 tool call limit, this will help you avoid reaching that.
> -   Consider using plan mode to plan out larger pieces of work
> -   Reference stories, record types, plans, and resources by `@` mentioning them in the App builder.

**Prompts are important.** Clear inputs lead to better results. Consider these:

- What you want your App to do, including any specific features or functionality.
- Whether the app should be powered by real data, and if it should connect to an existing story.
- The intended look and feel. You can upload mockups, references, or describe the desired design direction.
- Whether you want the App builder to generate [endpoints](https://tines.com/docs/apps/creating-apps/#endpoints) for you. Specify this in your prompt and it will be handled automatically.

### Preview

The Preview tab displays the visual output of your generated app. It is a fully interactive sandbox where you can interact with your application as it runs.
Using the built-in browser controls, you can refresh the view, navigate back and forward, and preview your App across different viewport sizes (Desktop and Mobile).

### Code

The Code tab contains all of the React code generated for your App. From here, you can browse files, search within the codebase, and edit code directly. Save any manual edits with `cmd + s`/ `ctrl + s` for individual files, or across all files with `cmd + option + s`/`ctrl + alt + s`.

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

> **TIP:**
> You can search for files names or within the files content, by using the toggle
> 
> ![](https://www.datocms-assets.com/55802/1780936648-search_file_contents_image.png)

### Endpoints

Endpoints are what connects your App to Tines workflows. Send data to or receive data from a story using endpoints.

App endpoints can be created in multiple ways:

#### **Workbench-generated endpoints**

Workbench can automatically create endpoints for you. When building something new, Workbench acts as a companion that can quickly generate endpoints within existing stories or spin up new, dedicated stories for your App. From your App, you can ask Workbench to create or use endpoints, and everything is handled within the conversation.

![App endpoints generated by Workbench](https://www.datocms-assets.com/55802/1780933872-workbench_generated_endpoints.png)

> **INFO:**
> Working with [change control](https://www.tines.com/docs/stories/change-control/) enabled? You'll be presented with the option to create a draft story for your endpoints.
> 
> ![](https://www.datocms-assets.com/55802/1780951799-create_change_control_draft_from_app_builder.png)

#### **Manually created endpoints**

You can manually define endpoints, either in an existing story or in a new one. If you’ve already built a workflow you want to expose in your App, you can enable its inputs and outputs as App endpoints so your App can interact directly with the story and its data. Alternatively, you can still use Workbench to help set these up more quickly.

To manually define endpoints:
- Navigate to the right panel on a storyboard → `Interfaces` section → `App endpoints
`- Ensure you have a webhook action to set as an entry point, and a message-only event transform action to set as an exit point. The entry point converts to an endpoint upon configuration.

##### Reuse recent response

For endpoints that return the same result for the same input, you can reuse a recent response instead of running the story again on every call. This lowers latency and cuts down on repeated story runs.Reuse is configured on the endpoint's entry point (the app endpoint action). 

Open the action and set `Reuse recent responses` to one of:

-  `Off` (default) — every request runs the story.
- `Per user` — each caller reuses only their own previous responses. Use this when responses depend on who is asking.
- `Per app`— one response is shared across every caller that sends the same input. Use this for data that is the same for all users.

When you choose Per user or Per app, set `Reuse responses for (minutes)` to control how long a response can be reused before the story runs again. 

A stored response is reused only when a later request sends exactly the same input. Tines invalidates stored responses automatically whenever you edit the story, and reuse never applies while you are testing a draft — those runs always run fresh. To discard stored responses manually, open the entry action, navigate to the status tab and select `Clear` . The next matching request then runs the story again.

![Displays configuration settings for App endpoints on a Tines storyboard](https://www.datocms-assets.com/55802/1780937078-manually_configure_app_endpoints_from_storyboard.png)

The Endpoints tab provides a consolidated view of all endpoints used by your App to communicate with one or more underlying stories. This view inside your App helps maintain context and provides visibility into key integrations. From here, you can open any endpoint’s associated story in a new tab to inspect the underlying workflow in more detail.

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

### Routing

Routing allows you to create multi-page Apps, and helps users of your App navigate between different views.

- Apps can have multiple pages, with their own unique URLs
- You can deep link to a URL, or refresh the page and it will persist
- Apps can consume the path, and URL parameters
- Routing is fully supported within Cases and Collections (you won't see the URL)

![Gif image of navigating through a multi-page App with routes](https://www.datocms-assets.com/55802/1780944904-routes.gif)

#### 
How to use routing

The App builder has full context of routing. Simply prompt to build a multi-page App.

### Publishing and versioning in Apps

Apps use **manual versioning**, so you control when changes go live. Each version becomes a **restore point** that can be accessed or rolled back to at any time. Experiment and iterate freely; your changes won't affect live Apps until you publish. Note: Versions do not include workflow changes.

Publish a version live to make it visible to your App's end users by clicking the **Publish** button at the upper-right-hand corner of your builder window.

![Gif of publishing an App](https://www.datocms-assets.com/55802/1780935545-publish.gif)

### Draft state

- All changes made to an App exist in a **draft state**
- Draft changes are **not live**
- Users can freely edit prompts, logic, and configuration without affecting end users

### Version history

- Manually save a version by clicking the arrow next to the **Publish** button at the upper-right-hand corner of your builder window -> Save version
- Publishing directly from a previously unsaved draft will create a version 
- Versions are stored in chronological order
- Older versions can be reverted to, renamed or deleted entirely
- Compare versions side by side

### Published version

- A **published version** is the version of your App that is made live and visible to your end users
- Published versions can be renamed
- Unpublish your live App by clicking the arrow next to the **Publish** button at the upper-right-hand corner of your builder window -> Unpublish

[Overview of saving App versions and publishing](https://stream.mux.com/MMYk1vyAWcjg8ZoRlI022bIb3aVNJO00Wn/high.mp4)

[Comparing App versions](https://stream.mux.com/h00iF8fqMHnAMjiHaM98altYs2SM2B5jN/high.mp4)

### Restoring versions

- Users can revert/rollback an App to any previously saved version
- Restoring replaces the current draft with the selected version 
- Restoring does not delete newer versions
- Discarding draft changes will revert to the latest saved version



> **INFO:** Working with [change control](https://www.tines.com/docs/stories/change-control/) enabled? If you have draft endpoints, you'll see a banner guiding you to review these before you can publish your App.

![Draft endpoints banner](https://www.datocms-assets.com/55802/1780989008-draft_endpoints_view_endpoints_tab.png)

![Draft endpoints warning message from Publish area](https://www.datocms-assets.com/55802/1780989029-draft_only_endpoints_from_publish_button_view.png)

### Admin settings for Apps

The Apps feature access can be enabled or disabled tenant-wide, or for specific teams.
Tenant owners can access `https://your-tenant-domain/settings/apps` to enable and disable the Apps feature.

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

## 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.

## Controlling who can view and edit Resources

Access to a resource is set by a team member's [role](https://www.tines.com/docs/admin/teams#roles), not on the resource itself. A resource is visible to anyone with read access to the team — and unlike credentials, its contents are shown in full.

- **Viewer** — read-only. Can open and reference a resource, but can't edit, move, or delete it. Use this for view-only access.
- **Editor** — can create and update resources.
- **Team admin** — can also move, delete, and change sharing settings.

With [custom roles](https://www.tines.com/docs/admin/user-administration/custom-roles), enable **Read** and leave **Resources → Update** and **Resources → Manage** off for view-only access. To stop everyone from editing a resource, lock it instead (see [Resource locking](https://www.tines.com/docs/resources#resource-locking)).

## 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)

## Handling concurrent updates

When several story runs update the same resource at the same time — for example, stories that run on a schedule at overlapping intervals, or a story that processes events in parallel — their changes can overwrite one another. Tines does not queue writes to a resource, so if two runs each read a resource, modify it, and write the whole value back, whichever run writes last wins and the other run's change is lost.

### Update individual elements

Rather than replacing an entire resource value, use the [append](https://www.tines.com/docs/api/resources/append-element), [replace](https://www.tines.com/docs/api/resources/replace-element), and [remove](https://www.tines.com/docs/api/resources/remove-element) element endpoints. Each of these changes a single array element or object key on the server, so you don't need to fetch the current value first. This avoids the lost-update problem that happens when multiple runs read and rewrite the whole value.

### Compare-and-swap (conditional updates)

The [replace](https://www.tines.com/docs/api/resources/replace-element) and [remove](https://www.tines.com/docs/api/resources/remove-element) endpoints accept an optional **if_value** parameter. The operation only proceeds if the current value at the specified key or index still matches **if_value**. Otherwise, the request returns a **422** error that includes the current value, and the resource is left unchanged.

This lets you make conflict-free updates without a separate read. A common use is a counter: read the current count, then send a replace with the new count and **if_value** set to the value you read. If another run changed the value in the meantime, your request fails and you can retry with the latest value.

## 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.

### Components of a case

Within the case structure are various components to display and interact with case data. This article 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

The case interface uses a visual hierarchy to help users naturally consume case information: high-level information appears at the top left, with detailed content toward the bottom right.

 When you open a case, start at the top-left to orient yourself with the case's context, then work your way down and to the right to review content and access tools.

Tines cases blend two concepts to deliver a best-in-class experience:

- **Case structure** – the layout of a case to optimize context gathering.
- **Case elements** – the properties, primitives, content, and activity that fill the case structure.

## Case structure

Every case follows a consistent, yet semi-configurable layout that ensures case details can be quickly referenced regardless of where that case originated.

- **Navigation **– A table-of-contents outline which can be used to quickly scroll to, highlight, or re-order case content.
- **Attributes** – The core classifiers of a case such as name, tags, priority, assignee, and details about the case lifecycle.
- **Content** – A markdown-based content editor/viewer which contains a fixed "description" section, a configurable carousel of blocks, followed by a reference of attached records.
- **Interaction sidebar** – A series of customizable and collapsible sections that serve as reference and interactivity points of a case.

![](https://www.datocms-assets.com/55802/1784086053-cases-structure-v7.jpg)

---

## Case elements

The elements of a case are organized into a number of categories:

1. **Primitives** – The core, built-in structural elements that every case has by default.
2. **Properties** – The contextual, user-defined fields of the case.
3. **Content** – The customizable and formatted specifics of the case.
4. **Interactions **– The activities that have occurred within a case or actions that can be triggered from within a case.
5. **Relationships** – The links between a case and other cases, or with attached records.
6. **Controls **– The access, requirements, and controls of a case.

Together, the structure and elements give you a flexible but predictable surface you can navigate, extend, and automate against. This page explains what each layer contains and links to detailed documentation for each component.



### Primitives

Primitives are the core, built-in fields that every case has by default. They're native to the case object and can't be removed.

- **Team:** the team the case belongs to ([learn more](https://www.tines.com/docs/teams/))
- **Group**: the group within the team the case is currently assigned to ([learn more](https://www.tines.com/docs/case-groups/))
- **ID:** globally unique identifier for the case
- **Status:** the current state of the case using custom statuses you define
- **Priority & SLA: **the importance of the case using `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, or `INFO` which also drives SLA timers
- **Assignees:** select the user(s) assigned to work the case, or move a case to another team or group
- **Subscribers: **users who have been mentioned in the case content or a comment, or have explicitly subscribed to a case to receive update notifications
- **Name:** the title of the case
- **Tags:** labels for categorization and filtering the case
- **Author:** who created the case
- **Timestamps:** describing when the case was created, last updated, and marked resolved

![](https://www.datocms-assets.com/55802/1784086234-cases-primitives.jpg)



### Properties

Properties are user-defined or system-augmented layers that add context on top of primitives. Most can be defined manually within a case or updated via story automation.

- **Fields:** customizable, typed, and searchable data fields attached directly to a case (via case inputs), for example IP address, hostname, or alert source ([learn more](https://www.tines.com/docs/case-fields/))
- **Metadata**: untyped, user-defined key/value pairs (hidden by default)

![](https://www.datocms-assets.com/55802/1784086249-cases-properties.jpg)



### Content

Primary content workspace where users or automation build and consume formatted markdown content. The content area consists of four components:

1. **Table of contents: **an outline of the case content with click-to-scroll, block highlighting, and drag-to-reorder functionality
2. **Description section: **a fixed-place, large text input that precedes content blocks
3. **Content blocks: **a collection of user-defined content containers with the following types: ([learn more](https://www.tines.com/docs/case-blocks/))
  
  - **Notes:** rich-text markdown content that consists of text, tables, code snippets, pasted images, and [dynamic mentions](https://www.tines.com/docs/formatting-references/) of many properties and primitives
  - **Attachments:** files associated with the case with an option to add annotations
  - **HTML: **specify basic HTML markup to render non-markdown content
  - **Groups**: grouping of blocks to improve readability and co-locate related content
4. **Content toolbar: **build your case content by selecting any of the listed block types

![](https://www.datocms-assets.com/55802/1784086267-cases-content.jpg)



### Interactions

Activities consist of two types: **monitoring** of activity happening within a case and  **triggering** actions to run Tines stories, or open pages, apps, and external URLs

#### Inbound activity

Historical activities surface a timeline of everything happening on a case, from automated alerts to a full audit trail to human-triggered actions.

- **Subscriber notifications:** alerts sent to case subscribers via email and in-app notification when something changes, for example a new comment, status update, or assignee change
- **Activity:** a full chronological audit trail of every change, comment, file, and status update on the case
- **Comments:** collaborative discussion thread that supports rich text markdown, references to case content, mentions, and file attachments (displayed within the "Activity"  section)
- **Tasks:** actionable checklist items with assignees ([learn more](https://www.tines.com/docs/tasks/)), also displayed with a progress indicator at the top of the case

#### Outbound actions

- **Case actions: **customizable buttons that trigger Tines stories, open pages and apps in an embedded sidepanel, or open external URLs directly from a case

![](https://www.datocms-assets.com/55802/1784086297-cases-activity.jpg)



### **Relationships**

Relationships can occur between two cases, or between a case and record.

- **Case links: **add or view linked cases, along with their status and a hover-summary
- **Records reference: **view records attached to a case, organized by record type

![](https://www.datocms-assets.com/55802/1784086309-cases-relationships.jpg)



### **Controls**

Control how a case functions, the requirements for completing a case, or who has access to case content.

- **Case options: **hide or show the TOC, interaction sidebar, expand the content to full page width, and set all blocks to expanded or collapsed
- **Case requirements**: formulaic conditions that must be completed before a case can be closed (displayed within the "Tasks" section)

#### Additional controls not shown

- **Fields section: **select which fields are "sensitive" to [restrict view access](https://www.tines.com/docs/sensitive-case-fields/) to users with the correct role, and define sort order
- **Actions section: **option to open web URLs and Tines apps or pages in a separate tab
- **Linked cases section**: option to group linked cases by status, and to open linked cases in a new tab

![](https://www.datocms-assets.com/55802/1784086327-cases-controls.jpg)

####  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

- [Content formatting and references](https://www.tines.com/docs/formatting-references/) 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.

##### Case table of contents

Use the table of contents to quickly move through case content and manage the case structure.

### Jump to a section

Click any entry in the table of contents to navigate to that heading, case block group, or case block. The content area scrolls automatically to display your selection:

-   **Case description**: Jump between headings within the description
-   **Note blocks and groups**: Jump between each note block or group of blocks

![](https://www.datocms-assets.com/55802/1770082562-toc-jump.gif)

### Focus on specific sections

To hide everything except the case content you want to review:

1.  Hover over a section in the table of contents
2.  Click the spotlight icon that appears to focus
3.  Click the spotlight icon again to restore the full view

![](https://www.datocms-assets.com/55802/1782269803-cases-toc-spotlight.png)

> **TIP:** You can spotlight multiple items at once by clicking the icon on more than one section or block.

### Reorder sections by dragging

### 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.

![](https://www.datocms-assets.com/55802/1782269788-cases-toc-drag-and-drop.png)

### 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.

##### Case blocks

Case blocks let you divide case content into discrete, reorderable sections below the description — each with its own title, type, color, and visibility settings.



### When to use blocks

Use blocks when a case has enough content that a single description isn't sufficient. Blocks let you create clearly separated sections like "Evidence," "Timeline," "Remediation Steps," or "Approval."

---

##### 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.



### Types of blocks

Cases support several block types. Some are **multi-instance** (you can add many per case) and some are **single-instance** (one per case, automatically created or added once).

| **Toolbar button** | **Icon** | **Details** |
| --- | --- | --- |
| **Note block** | ![](https://www.datocms-assets.com/55802/1769636924-block-note.png) | Add helpful notes to your case that can be titled, colored, and formatted. The content of a note block is included in search results. |
| **HTML block** | ![](https://www.datocms-assets.com/55802/1769636924-block-html.png) | A block that supports rendering basic HTML syntax. The content of an HTML block is not included in search results. |
| **Attachment block** | ![](https://www.datocms-assets.com/55802/1769636924-block-file.png) | A block to add one or more attachments. Each attachment can be annotated with a direct comment. |
| **Table block** | ![](https://www.datocms-assets.com/55802/1769636924-block-note-with-table.png) | A note block that's pre-formatted with a table. |
| **Code block** | ![](https://www.datocms-assets.com/55802/1769636924-block-note-with-code-snippet.png) | A note block that's pre-formatted with a code snippet. This text can be formatted as text, C, C++, CSS, HTML, Java, JavaScript, JSON, Markdown, Python, SQL, and YAML. |
| **Group of blocks** | ![](https://www.datocms-assets.com/55802/1769636924-block-group.png) | 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. |

💡 **Tip:** Single-instance blocks are created once and cannot be duplicated. If you don't see an option to add one, it already exists in the case.

---

### Adding a block

Click the buttons in the **toolbar at the bottom of the case** to add a block below the description. The new block appears at the end of the case content by default.

---

### Block titles

Every block has a title (max **100 characters**) that appears in the table of contents. Set a clear, descriptive title — it's the primary way teammates navigate the case structure.

---

### Block color

Note blocks can be assigned a background color to visually distinguish sections. Available colors include standard palette options visible in the block settings.

> **TIP:** Use color consistently across your team. For example: blue for evidence, red for critical findings, green for completed remediation, gold for pending review.

---

### Block groups

Block groups let you nest related blocks under a collapsible parent. In the table of contents, a block group appears as an expandable section.

- Create a block group via the toolbar.
- Drag existing blocks into the group, or set the `block_group_id` via the API.
- Child blocks within a group can be reordered independently.

Block groups are especially useful for phases of an investigation (Triage → Analysis → Remediation) or for organizing large cases with many blocks.

---

### Reordering blocks

Blocks can be reordered by:

- **Dragging** in the table of contents sidebar.
- **Using the API** to set block position.

> **IMPORTANT:** The description section is always fixed at the top of the case. It cannot be reordered or placed below blocks.

---

### Hiding blocks

Select **Hide** from the block's options menu to hide it from the main case content view. Hidden blocks:

- Are still listed in the table of contents (dimmed).
- Can be **spotlighted** by clicking them in the TOC — this temporarily displays their content inline without unhiding them.
- Remain accessible via the API.

Use this to keep raw data, verbose logs, or intermediate evidence accessible without cluttering the case view.

---

### Sensitive blocks (hidden from exports)

Note blocks and HTML blocks have a **Sensitive** toggle. When enabled:

- The block is labeled **"Sensitive — Hidden from exports."**
- The block's content is **excluded from PDF exports** of the case.
- The block remains fully visible and editable within the case itself.

> **WARNING:** This is not an access control — all case viewers can see sensitive blocks. It only controls export behavior.

---

### PDF export behavior

When a case is exported to PDF, the export includes:

- The case **description**
- **Note blocks** (and note blocks nested inside block groups) — *unless* marked as sensitive
- Case details, fields, tasks, activities, and records

The following block types are **not rendered in the PDF body**: file blocks, HTML blocks, linked cases, closure conditions, metadata, and case action blocks. If content must appear in a PDF export, place it in the description or a non-sensitive note block.

---

### Table of contents interaction

The table of contents sidebar reflects the full block structure:

- **Description** headings and **block titles** are listed.
- **Block groups** are collapsible.
- **Hidden blocks** appear dimmed but are clickable (spotlight behavior).
- Click any item to scroll directly to that section.

The TOC can be toggled between **persistent** and **peek** mode via the **Contents** button at the top of the sidebar. In peek mode, the sidebar hides until you hover near the left edge.

---

### Automation: blocks via the API

Blocks can be created, updated, and deleted programmatically:

- **Create a block** — `POST /api/v2/cases/{case_id}/blocks` with `block_type`, `title`, optional `block_group_id`, `position`, `hidden`, and content.
- **Update a block** — `PUT /api/v2/cases/{case_id}/blocks/{block_id}` to change title, position, visibility, group membership, or content.
- **List blocks** — `GET /api/v2/cases/{case_id}/blocks` to retrieve all blocks in a case.

Use action templates from the template library to integrate block management into your story automations.

---

### Best practices

- **Standardize block structures** using case templates so every case of the same type starts with the same sections.
- **Use block colors consistently** across your team to create visual conventions.
- **Hide verbose blocks** (raw logs, API responses) to keep the case readable — they remain accessible via the TOC.
- **Use block groups for investigation phases** (Triage, Analysis, Remediation) or logical groupings (Evidence, Actions, Approvals).
- **Mark sensitive blocks** to prevent confidential content from appearing in PDF exports.
- **Max 50 blocks per case** — if you're approaching this limit, consolidate related content or use block groups.

##### Content formatting and references

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>Surface</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.

[Video](https://stream.mux.com/1m6IKK9U6H2SQO02Eyq4HBHxheX4Tuw00U/high.mp4)

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 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/1782266413-cases-sidebar.jpg)

### **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.

[Video](https://stream.mux.com/UemeLyYOop3cIPHIlha88asFpbi9rc5x/high.mp4)

| **Sidebar sections** | **Description** |
| --- | --- |
| **Activity & comments** | 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 `/` shortcut. When mentioning another user in a comment with the `@` shortcut, they will automatically be subscribed to the case. |
| **Case actions** | 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 `@` shortcut. |
| **Case fields** | 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 `@` shortcut and the field name. Once a field is referenced in the case content, it can be interacted with and updated. |
| **Linked cases** | Display case links to other cases. Add or remove linked cases here. Hovering on a case shows a summary of the target linked case. |
| **Tasks** | Display **tasks** and **closure requirements** 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 [API](https://www.tines.com/api/cases/update/). |

> **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/).

#### 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><th>Keyboard shortcuts</th><th><div><div>Description</div></div></th></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><th>Keyboard shortcuts</th><th><div><div>Description</div></div></th></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>

### 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.

[Video](https://stream.mux.com/3KGjEcAD1W7pHbKTq01LEEoQDKgKh9b2B/high.mp4)

##### 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:

[Video](https://stream.mux.com/8bBeIMsto5cK4P1JQ6mSJGAY02zAeUQEZ/high.mp4)

## 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.

[Video](https://stream.mux.com/KVBV42hFrLRYcdZDh1SXAfNVCu99WM2l/high.mp4)

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`.

[Video](https://stream.mux.com/kpGGr00PcM7EEUs02PKEa01eRDErTRf00RHd/high.mp4)

## 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.

[Video](https://stream.mux.com/cPtFzfGn3TVHtQMaj00ahOzzndvvMYOoS/high.mp4)

## 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.

[Video](https://stream.mux.com/mR1qpU1VJhcS2BAM3yhORG14L4AVovLx/high.mp4)

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.

[Video](https://stream.mux.com/EJv00jMiU2km4FfYaIUxaKOIHCIAfBkXM/high.mp4)

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.

[Video](https://stream.mux.com/ZFuoLPhORBJToDyN9VxjUKVaqUfki8c7/high.mp4)

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, icon and button text to make it clear the exact call to action expected of users. 

You can also add query parameters to pass data into the webhook or page. These can include formulas which can be set to run when the case is created from the storyboard or when the button is triggered from within the case.

#### 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.

| **Permission/Team Role** | **Team Admin** | **Editor** | **Viewer** | **Case manager** | **Dashboard manager** |
| --- | --- | --- | --- | --- | --- |
| Read cases | x | x | x | x |  |
| Create / update cases | x | x |  | x |  |
| Create / update records | x | x |  |  |  |
| Create / update dashboards | x | x |  | x | x |
| Manage team members (add / remove) | x |  |  |  |  |
| Share objects (stories, resources, credentials, templates) | x |  |  |  |  |
| Move objects (stories, resources, credentials) | x |  |  |  |  |
| Delete objects (stories, resources, credentials, events, records) | x |  |  |  |  |
| Delete templates | x | x |  |  |  |

## 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 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/):

| **Permission** | **Description** |
| --- | --- |
| Assign tasks | Assign a task to one or more users within the Team or case group |
| Complete tasks | Mark tasks complete/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 |

## Additional information

- Find the Tasks API documentation at [tines.com/api/cases/tasks/create](https://www.tines.com/api/cases/tasks/create)

#### 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/)
- 
-

##### 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
- [Custom roles](https://www.tines.com/docs/custom-roles/)
- [Tines API – case inputs](https://hq.tines.io/docs/api/)

### Creating a case

Creating a case in Tines can occur in two ways:

1. Manually from the case list, case board view, or record type view
2. Automatically using the "create case" tile when building a story



## Manual case creation

You can create a case manually from the case list or board view. Choose between opening a blank case or starting from a template.

- Case title is the only required input
- Formulas cannot be used (except for "case requirements")
- Attachments must be added after the case is created

After selecting an option, the resulting case will be created in the currently selected team or [case group](https://www.tines.com/docs/case-groups/). Cases can later be moved between case groups.

![](https://www.datocms-assets.com/55802/1784613095-doc-manual-case-creation.png)

> **TIP:** While in the editor, you can save the current configuration as a new template.



### Starting from a blank case

When building a case manually from scratch, you can define nearly every aspect of case elements including content, primitives, and properties.



### Using templates

Copy the content of an existing template, then create your case manually from there. All elements of case can be configured, but subsequent changes to the template are not reflected in the target case.



## Automated case creation from a story

Automatically create cases by using the `create case` tile in your storyboard. This method provides a few advantages such as:

- Link a template to more easily manage global modifications to case creation
- Leverage overrides to make local modifications to specific story tiles
- Incorporate story data into the case using formulas
- Manage updates to case content via change control



### Using templates or a blank case

Start building by selecting the `create case` tile from the story menu. You will then be prompted to select how to create the case:

**Blank case** - Start from an empty case and freely define all case elements.

**Copy a template** - Start from a template and freely define all case elements. Subsequent changes to the chosen template will NOT be reflected in the tile's configuration.

**Linked template **- Start from a template. Make changes to "override" the linked template's values in that specific story tile. 

- Case blocks cannot be added, modified, re-ordered, or removed
- Unlink an entire case section to make changes to specific items in that section



[Video](https://stream.mux.com/vqnlO1l77jpCi4hhQDFpRN01wjUH01nZAr/high.mp4)



### Using overrides

When using a **linked template**, you can modify the template's values using "overrides". These are stored as local changes to that specific story tile. The resulting case will display all overrides blended with remaining  values from the linked template.

*Overrides are not applicable when building blank cases or copying a case template.*

[Video](https://stream.mux.com/PdOPi3wAFoaym00txOE5J8QHlun5Hepto/high.mp4)

#### **Override basic case primitives**

**You can easily override a template value by simply inputing a new value on a case elemnt. A purple dot will be shown next to the updated primitive's title to indicate it's currently overridden.**

#### Override case sections

To modify any item in the following case sections, you must "unlink" the section first. Doing so sets the entire section as  "overriden", thus allowing you define all items in the section.

- Fields
- Case requirements
- Tasks
- Actions
-  Metadata

> **NOTE:** To override a specific item in a section, the entire section must be unlinked. All items in the section are considered "overriden". Reverting an unlinked section will revert the entire section back to the linked template.

#### Override case content

Case content may or may not be overriden depending on the element.

✅ Case **description** can be overriden, but will unlink the entire description content from the linked template. You cannot select specific lines within the description to override – it is all or nothing.

🚫 Case **content blocks** can NOT be overriden. The case will be created with the content blocks as defined by the linked template. You can add, update, and delete content blocks using subsequent story actions *after* the case is created.

#### **Reverting an override**

Hoverring on the purple "override" dot will show a selectable "revert" button. Click this to clear the modified value and revert the case element to use the linked template's value.



### Inserting formulas

Formulas can be used when automatically creating a case. Leverage upstream event data or Tines formula keys to build dynamic expressions.

- Opt to use formulas across case primitives via the displayed toggle or the `ctrl + space`  shortcut
- Formulas can be inserted into the description or case blocks using the `/` formating menu option

> **TIP:** You can use a formula to insert markdown content into the case description or a case note block. The resulting case will display the rich-formatted text in the appropriate content area.

[Video](https://stream.mux.com/tcBziulVjI7PTgQeKbLX9soZN3k02x3sy/high.mp4)

### Case settings

#### 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 receive all case notifications, however, you can configure which notifications the webhook receives from the dropdown.

[Video](https://stream.mux.com/KzjjVF3T1xNZsOLAmlLaBKC01EM8AvaqF/high.mp4)

### 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

| **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 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 with cases access

For more information on roles and permissions, see [user administration](https://www.tines.com/docs/user-administration/). 

<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>

> **TIP:** 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

Below are the various individual permissions

### Core case management

Case operations

- Basic case management
- [View sensitive information](https://www.tines.com/docs/sensitive-case-fields/) (Fields)
- Bulk case updates
- Closure conditions
- Saved views
- Case templates

Team settings

- [Fields](https://www.tines.com/docs/case-fields/)
- SLAs
- Status
- [Case notification webhooks](https://www.tines.com/docs/case-notifications/)

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 11.8103%;"><col style="width: 11.8103%;"><col style="width: 25.5102%;"><col style="width: 14.881%;"><col style="width: 19.6051%;"><col style="width: 16.2982%;"></colgroup><tbody><tr><th>Group</th><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Core<br></strong></td><td>Create</td><td>Create new cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong></strong></td><td>Delete</td><td>Permanently delete cases</td><td>–</td><td>–</td><td>–</td></tr><tr><td><strong></strong></td><td>View</td><td>List and view cases</td><td>✓</td><td>✓</td><td>✓</td></tr><tr><td><strong>&nbsp;</strong></td><td>Update</td><td>Update case name and basic fields</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Bulk manage cases</td><td>Use the bulk update tool (requires component permissions to specify what can be updated in bulk)</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Update case security settings</td><td>Update team-level case security settings</td><td>–</td><td>–</td><td>-</td></tr><tr><td></td><td>View sensitive information</td><td>View case fields marked as sensitive</td><td>–</td><td>–</td><td>–</td></tr><tr><td><strong>Closure conditions</strong></td><td>Override closure conditions</td><td>Option to override case closure conditions when updating status</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Saved Views</strong></td><td>Create</td><td>Create a saved view</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>&nbsp;</strong></td><td>Update</td><td>Update a saved view</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Delete</td><td>Delete a saved view</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Templates</strong></td><td>Create</td><td>Create a new case template</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Update</td><td>Update existing case templates</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Delete</td><td>Delete a case template</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Fields</strong></td><td>Create</td><td>Create a new field to be available for the team</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>&nbsp;</strong></td><td>Update</td><td>Update an existing feild available to the team - name, sensitivity, value options, folder location</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>&nbsp;</strong></td><td>Delete</td><td>Delete a field definition</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong></strong><strong>SLAs</strong></td><td>Create</td><td>Set an SLA's duration for each priority type</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>&nbsp;</strong></td><td>Update</td><td>Update an SLA's duration for a specific priority type</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>&nbsp;</strong></td><td>Delete</td><td>Remove the SLA duration on each priority type</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Status</strong></td><td>Create</td><td>Create a new status, define the color</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Update</td><td>Update existing statuses, their order in the menu, and color</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Delete</td><td>Delete an existing status</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Webhook </strong>(Notifications)</td><td>Create</td><td>Create new webhook and define which notification types trigger it</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Update</td><td>Update which notificaiton trigger a webhook</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Delete</td><td>Delete notifications</td><td>✓</td><td>✓</td><td>–</td></tr></tbody></table>





### Case components

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 11.8608%;"><col style="width: 11.8608%;"><col style="width: 24.4478%;"><col style="width: 15.2496%;"><col style="width: 20.0908%;"><col style="width: 16.702%;"></colgroup><tbody><tr><th>Group</th><th>Permission</th><th>Description</th><th>Editor</th><th>Case manager</th><th>Viewer</th></tr><tr><td><strong>Assignee</strong></td><td><pre><strong>Update</strong></pre></td><td>Add or remove assignees</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Subscribers</strong></td><td><pre><strong>Create</strong></pre></td><td>Subscribe users to cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td><pre><strong>Delete</strong></pre></td><td>Unsubscribe users from cases</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><b>Priority</b></td><td>Update</td><td>Change case priority</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td><strong>Statuses</strong></td><td>Create</td><td>Create new statuse, define color</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Update</td><td>Update existing statuses, order of statuses, color of a status</td><td>✓</td><td>✓</td><td>–</td></tr><tr><td></td><td>Delete</td><td>Delete existing statuses</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>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 field values

<table border="1" style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-size: 15px; font-weight: 400; 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>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 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>



## 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 limits

See the [case overview ](https://www.tines.com/docs/overview/)for a concise reference to each of these sections.

## 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 content

Maximum case block limits per case:

### Case description

| Type | Limit |
| --- | --- |
| Description sections | 1 |
| Description length | 640,000 characters |

### Case blocks

| Type | Limit |
| --- | --- |
| Case blocks of any type (Note, HTML, File, Table, Code) | 50 blocks |
| Block groups | 15 groups |
| Blocks per group | 50 blocks |
| Block title length | 100 characters |
| Block content length | 15,000 characters |

### Files

| Type | Limit |
| --- | --- |
| Case file types | 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 |
| Maximum file size | 60 MB per file *If you need to share a larger file please link to it from a file hosting service.* |
| Number of files per case block | 50 files |
| Cumulative file sizes | 3 GB |

---



## Case elements

Maximum limits per case for primitives, properties, interactions, relationships, and controls:

### Primitives

| Type | Limit |
| --- | --- |
| Case name | No character limit |
| Assignees | 10 assignees |
| Subscribers | No limit |
| Tags | 50 tags |

### Properties

| Type | Limit |
| --- | --- |
| Fields | 100 fields |
| Field groups | 100 groups |
| Field value length | 1,000 characters |
| Metadata keys | 100 keys |
| Metadata value length | 1,024 characters |

### Interactivity

| Type | Limit |
| --- | --- |
| Case action buttons | 25 action buttons |
| Tasks | 50 tasks |
| Task title length | 100 characters |
| Comments | No limit |
| Comment length | 640,000 characters (per comment) |

### Relationships

| Type | Limit |
| --- | --- |
| Linked cases | 150 case links |
| Attached records | 1,500 records |
| Record types associated | No limit |

### Controls

| Type | Limit |
| --- | --- |
| Case requirements | 50 conditions |
| Case requirement title length | No limit |

---



## General text length

Maximum text limits per case:

| Type | Limit |
| --- | --- |
| Comment length | 640,000 characters (per comment) |
| Description length | 640,000 characters |
| Field value length | 1,000 characters |
| Metadata value length | 1,024 characters |
| Block title length | 100 characters |
| Block content length | 15,000 characters |
| Task title length | 100 characters |

---



## Bulk actions

Maximum bulk selections allowed when managing multiple cases:

| Type | Limit |
| --- | --- |
| Selected cases | 50 cases |

---

## Case team settings

Maximum [case setting ](https://www.tines.com/docs/case-settings/)limits per team:

| Type | Limit |
| --- | --- |
| Total statuses | 10 statuses |
| Total fields | 1,000 fields |
| Total validations | 75 validations per field |



## 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)*. *

## *Records in practice*

*Records turn the data flowing through your stories into a structured, queryable table you can report on over time. A few common patterns:*

- * ****Alert triage and trend analysis****: capture each alert your stories process (source, severity, verdict, analyst decision) as a record. Over time the records table shows alert volume by source, false-positive rates, and severity trends you can chart on a dashboard.*
- ***Indicator (IOC) enrichment****: store the output of enrichment tools such as URLScan or VirusTotal (the indicator, its score, whether it was flagged malicious). Reuse the record later to avoid re-enriching the same indicator, and to monitor which sources produce the most true positives.*
- ***Phishing report tracking****: record every reported email (reporter, subject, verdict, action taken) to measure reporting volume, time to triage, and how many reports were confirmed malicious.*
- ***Joiner/mover/leaver auditing****: capture identity events (user, action, system, timestamp) so you have a durable, exportable audit trail independent of the source system.*

***Tip****: Capture the individual fields you'll want to filter, group, or chart on — not just a raw payload. Structured fields can be queried and visualized; artifacts (large text) cannot.*

## Capture a record

Drag a record onto the storyboard from the ‘tools’ menu. Then connect it to the upstream and/or downstream action. 

[add-record-to-storyboard](https://stream.mux.com/LKcKR8EbzjOaFR01PL5KypLk5wbZNm01X6/high.mp4)

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. 

[Video-Capture-Record-Existing-Type](https://stream.mux.com/krUlEtj2uGpDMRlbGroiS02gngt6AWrvQ/high.mp4)

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. 

[Video](https://stream.mux.com/uz00ScerOJAWEJIL21eVjOckadlbCDcHY/high.mp4)

> **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.

[Video](https://stream.mux.com/ngN6aPYlXkUhsl6YYAjNFtdQyZapXV4l/high.mp4)

### 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. Contains (case insensitive)
  6. Starts with (case insensitive)
  7. Ends with (case insensitive)
  8. Is empty
  9. Is not empty
  10. Greater than or equal
  11. Greater than
  12. Less than or equal
  13. Less than
  14. Is true
  15. 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. 

***Note:**** Contains, Starts with, and Ends with are available for text and text (fixed values) fields only. The search value must be at least three characters, and you can apply only one of these three operators at a time.*

### 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. 

> **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. 

[Video](https://stream.mux.com/2AWkJUub5E02LNjBNNgYdS6D7VXf2tYH7/high.mp4)

**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.

[Video](https://stream.mux.com/hreZZ01ezt6oQLjKT008f9tZT7jT02su8M2/high.mp4)

## "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.

[Video](https://stream.mux.com/cbKZYpdgHI202DIQ2KygS6IAdFYQpS2pX/high.mp4)

## 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.

[Video](https://stream.mux.com/Bj4AfQ2R00Xt3fRvvvujnESgpwoJSMn4b/high.mp4)

### **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

[Video](https://stream.mux.com/yDKRWh1bPisyTaqdTYoeC56vvqkAY1Ec/high.mp4)

### 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/). Personal teams can have 10 record types max, which do not count towards the limit. |
| **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><span>• Self-hosted tenants</span></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><span>• Cloud tenants<br></span><span>• Self-hosted tenants</span></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><span>• Cloud tenants<br></span><span>• Self-hosted tenants</span></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. 

- **Record types**: The maximum number of record types you can create across all teams in your tenant.
- **Records per record type**: The maximum number of records for an individual record type.
- **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.

### Record limits by license

Limits are determined by the license level and increase accordingly.

|  | **Starter** | **Essentials** | **Standard** | **Advanced** | **Enterprise Lite** | **Enterprise L1** | **Enterprise L2** | **Enterprise L3** | **All Self-hosted** |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Record types** | 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 limits for personal teams

Users can create record types and records within a personal team. The limits for records and personal teams are as follows:

- **Record types:** 10
- **Records per record type:** See license-specific limits
- **Max total records:**  See license-specific limits

> **NOTE:** **Record types** created in a personal team DO NOT count towards the tenant's total usage. However, **Records** created in a personal team DO count towards the "total records" usage.

## Record type configuration

Configurable limits within each record type.

| **Limit** | **Description** |
| --- | --- |
| Custom record limit | Configurable up to the **records per record type** limit specific to your license (see "Record limits by license" above). |
| Record retention | Configurable up to 10 years. Records without a configured retention are kept for the duration of the customer’s active license. |

## Record type limits

| **Limit** | **Description** |
| --- | --- |
| Charts per record type | 10 charts |
| CSV export size | 100k rows |
|  |  |

## Record charts

These limits apply to charts in a record type or within [Dashboards](https://www.tines.com/docs/dashboards/)

### Series limits

When choosing how many series items to display in a chart, you can choose up to the maximum. Any amount over the maximum will be shown in the "other" group when enabled.

| **Chart type** | **Default** | **Maximum** |
| --- | --- | --- |
| Bar chart | 10 | 100 |
| Pie chart | 10 | 50 |
| Stacked bar chart | 10 | 25 |
| Stacked chart | 10 | 25 |

## Record fields

| **Limit** | **Description** |
| --- | --- |
| Field definitions per Record Type | 50 |
| Validation options per field | 100 |
| Size of "Numeric" field type | Up to 7 places |
| Size of "Text" field type | 512 characters |
| Size of "Large text" (artifact) field type | 100k characters |
| Cumulative size of "Large text" fields in a single record | 750k characters |
| Validation options for fixed text fields | Up to 100 |

## Record API limits

The limitations when calling the [records API](https://www.tines.com/api/records/) are as follows.

| **Limit** | **Description** |
| --- | --- |
| Requests per minute (All endpoints except "Query") | 400 |
| Requests per minute (Query endpoint) | 200 |
| Page size per request | 500 |
| Max records per minute total | 200,000 |

## 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. 

[Video](https://stream.mux.com/iXBDIpNS00ZOJdktw015rWom23WK9RmF2W/high.mp4)

## 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)

## Start from a template

Tines includes sample dashboard templates to help you get started quickly.

1. On the Dashboards page, open the help panel on the right-hand side (it opens automatically for teams with no dashboards).
2. In the **Templates** section, click **Start with template** on the one you want.

Tines creates a new dashboard from the template and opens it, ready for you to rename or edit.

***Note****: Importing a template requires dashboards enabled on your tenant and permission to create dashboards (Team admin, Editor, Case manager, or Dashboard manager).*

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

*Dashboard templates (in the right hand panel)*

## 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

[Video](https://stream.mux.com/BArBKGd9fDA3AG3qi91DUu48olKGQMeq/high.mp4)

### 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

[Video](https://stream.mux.com/KEMOODTC6fbwkglG3rcRoKdR02tNYxKpn/high.mp4)

# Dashboard snapshots

Snapshots let you email a copy of a dashboard on a schedule — useful for recurring reports to stakeholders who don't log in to Tines. Pick who receives it, how often, and which charts to include.

### Create a snapshot

1. Open the dashboard and click **Snapshots** in the toolbar. (The dashboard must not be in edit mode — publish or discard any draft first.)
2. Select **Create new snapshot**.
3. Give the snapshot a name (for example, "Weekly dashboard report").
4. Add one or more recipient email addresses.
5. Choose the frequency (for example, every Monday, or every day) and the timezone. The next scheduled send times are shown so you can confirm them.
6. Choose which charts to include or hide.
7. Save. Use the active toggle to enable or disable the snapshot at any time.

A dashboard can have multiple snapshots (for example, a daily summary and a weekly report), each with its own recipients and schedule.

***Note****: Managing snapshots requires permission to manage dashboards (Team admin, Editor, Case manager, or Dashboard manager). Charts that can't be exported are excluded automatically — if every element is hidden or non-exportable, the snapshot has nothing to send.*

![](https://www.datocms-assets.com/55802/1783510205-dash-snap-screenshot.png)

*Dashboard snapshot modal*

### Dashboard limits

## Dashboards

The limitations when creating and configuring dashboards are as follows.

| Type | Limit |
| --- | --- |
| Number of dashboards per team or group | 20 |
| Number of charts per dashboard | 30 |
| Characters allowed in a dashboard title | 100 |
| Number of scheduled snapshots per dashboard | 10 |
| Recipients per snapshot | 50 |

## 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.

[Video](https://stream.mux.com/01yefi5aKWcb02uEqN68mxxnAgF800GaZHY/high.mp4)

## 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.

[Video](https://stream.mux.com/9KoZU01wJHdEeG00A02nYdJr2CilsHtWFM2/high.mp4)

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

| Element | Description |
| --- | --- |
| Context window | Model dependent  
*200,000 tokens  
**1 million tokens* |
| Max output | 8,192 tokens |
| Messages & tool runs per minute per tenant | 25 (EU tenants) 50 (US tenants) 100 (custom AI provider) |
| Input tokens per minute per tenant | 300,000 (EU tenants) 500,000 (US tenants) 1,000,000 (custom AI provider) |
| Default truncation limit (can be disabled) | 50,000 tokens |

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:

| **Provider** | **Base URL** | **API key** | **Custom models** | **Custom headers** |
| --- | --- | --- | --- | --- |
| [OpenAI](https://openai.com/api) | ✅ | ✅ | ✅ | ✅ |
| [Anthropic](https://www.anthropic.com/api) | ✅  
 | ✅ | ✅ | ✅ |
| [AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) (cloud + [self-hosted](https://www.tines.com/docs/self-hosting/additional-configuration/ai/)) | ✅ | ✅  
 | ✅ | ✅  
 |

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.

[Video](https://stream.mux.com/7r02E1ARRu94ouEByLsc4ulHct9CwXnaE/high.mp4)

### 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.

[Video](https://stream.mux.com/7ovn9jnKcGbkVbwh55fkVquH00b9N5NK4/high.mp4)

### **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 Workbench for Storyboard**

[**Workbench for Storyboard**](https://www.tines.com/docs/stories/workbench-for-storyboard/)** usage always comes from unallocated credits—this ensures that usage does not consume credits allocated for runtime AI usage in your teams.**

Under** Workbench for Storyboard 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/1782747445-ai-credit-allocations-and-limits.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.*

### Change Control

## Change control settings

![](https://www.datocms-assets.com/55802/1782484133-screenshot-2026-06-26-at-15-27-14.png)

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.
- **Enable requirements:** configure the conditions stories must meet before they can be submitted for review. When enabled, a requirements table appears alongside an **Add requirement** button.

![](https://www.datocms-assets.com/55802/1782484208-screenshot-2026-06-26-at-15-29-57.png)

Each requirement targets a specific story attribute — name, description, owners, tags, monitoring, or event retention — and defines a comparison condition and optional value. Requirements have two enforcement levels:

- **Required** — the story cannot be submitted for review until the condition is met.
- **Recommended** — the condition is surfaced as guidance but does not block requesting review.

Individual requirements can be disabled (greyed out in the table) without being deleted, which is useful for temporarily suspending a condition without losing its configuration.

![](https://www.datocms-assets.com/55802/1782484279-screenshot-2026-06-26-at-15-31-03.png)

### 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

By default, tenant owners have the responsibility for User administration across their tenant. In order to delegate some of these capabilities to other users, the USER_READ and USER_MANAGE tenant permissions should be assigned.

## 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.

[Video](https://stream.mux.com/HiQ3KJBl3Urn00vsolYaxUCPKs8b6oUUw/high.mp4)

## 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** |
| --- | --- |
| API\_KEY\_CREATE | 

Allows a user to create a personal API key. Only applicable if the tenant has enabled API key creation restriction.



 |
| 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



 |
| 

USER\_READ



 | 

View all users in your tenant, including their email, role, and team memberships.

 |
| 

USER\_MANAGE



 | 

Manage other (non-admin) users across your tenant, including:

-   Inviting users
-   Adding or removing users from teams
-   Changing a user's team roles (excluding TEAM\_ADMIN)
-   Updating a user's active status.  
      
    This permission does not imply read capabilites, and should be granted in combination with USER\_READ for best effect.







 |

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](https://stream.mux.com/SzC00S5PRBYHb42M3PQmJF8jFaWRmnD6b/high.mp4)

### 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.

[Video](https://stream.mux.com/fdx00fNU7R8eQ4vYAJsZ4wzXRIvTzLfUu/high.mp4)

#### **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

| **Permission/Team Role** | **Team Admin** | **Editor** | **Viewer** | **Case manager** | **Workbench user** | **Dashboard Manager** | **Dashboard Viewer** |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Read all objects (stories, resources, credentials, events, etc.) | x | x | x |  |  |  |  |
| Read cases | x | x | x | x |  |  |  |
| Read dashboards | x | x | x | x |  | x | x |
| Create / update stories | x | x |  |  |  |  |  |
| Create / update credentials\* | x | x |  |  |  |  |  |
| Create / update resources | x | x |  |  |  |  |  |
| Create/ update templates | x | x |  |  |  |  |  |
| Create / update cases | x | x |  | x |  |  |  |
| Create / update records | x | x |  |  |  |  |  |
| Create / update dashboards | x | x |  | x |  | x |  |
| Run an action | x | x |  |  |  |  |  |
| Export stories | x | x | x |  |  |  |  |
| Import stories | x | x |  |  |  |  |  |
| Publish / push stories | x | x |  |  |  |  |  |
| Manage team members (add / remove) | x |  |  |  |  |  |  |
| Share objects (stories, resources, credentials, templates) | x |  |  |  |  |  |  |
| Move objects (stories, resources, credentials) | x |  |  |  |  |  |  |
| Delete objects (stories, resources, credentials, events, records) | x |  |  |  |  |  |  |
| Delete templates | x | x |  |  |  |  |  |
| Author Workbench presets | x | x |  |  |  |  |  |
| Use Workbench presets | x | x |  |  | x | x |  |

*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. 

[Video](https://stream.mux.com/bbkYLDMSyGxas3AG6OFp01JsblyGhaRAs/high.mp4)

## 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.

[Video](https://stream.mux.com/D1n02uAGHg0159Ty02eWPCTzC6024miXXnS00/high.mp4)

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) in your domain registrar 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. 

You can read more about how we create custome sender emails in the [AWS docs](https://docs.aws.amazon.com/ses/latest/dg/mail-from.html#mail-from-set) .

> **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.

[Video](https://stream.mux.com/xu8xHFFZi5zQD5WKmvxC3O5gao4kQYXu/high.mp4)

### 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/).

[Video](https://stream.mux.com/LcYWmfX35uhgakc93500SJm7UqKZf2az3/high.mp4)

### 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

| Container requirements |
| --- |
| Image | tines/tines-tunnel |  |
| Container count | 1 |  |
| RAM | 2 GB | per container |
| Disk | 2 GB | per container |
| Architecture | x86\_64 |  |
| Logging | Log rotation considered. | Tines containers write logs to STDOUT/STDERR by default, so customers should capture and rotate these logs to mitigate disk storage and performance issues. |

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)

#### Running 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.

## Connectivity Requirements

The tunnel is built on [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) and requires connectivity to Cloudflare to function. No inbound ports are required. See [Connectivity requirements](https://www.tines.com/docs/connectivity-requirements/) for the firewall rules to allow.

#### Running the Tines Tunnel on 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

The tunnel is built on [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) and requires connectivity to Cloudflare to function. No inbound ports are required. See [Connectivity requirements](https://www.tines.com/docs/connectivity-requirements/) for the firewall rules to allow.

#### Connectivity requirements 

## 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. |

> **IMPORTANT:** 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/).

#### 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\`.

#### 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
```

#### 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.

### 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:

| **Destination** | **Port** | **Protocols** |
| --- | --- | --- |
| region1.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux |
| region2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux |
| region1.v2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux |
| region2.v2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux |
| api.cloudflare.com | 443 | TCP/HTTPS |
| updates.cloudflare.com | 443 | TCP/HTTPS |

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.

| **ENV Variable Name** | **Default** | **Description** |
| --- | --- | --- |
| `PORT` | 80 | Server HTTP port |
| `TLS_PORT` | 443 | Server HTTPS port |
| `FORCE_SSL` |  | Force usage of HTTPS protocol |
| `API_KEY` |  | `x-api-key` header value to check (not checked by default) |
| `TLS_CERT` |  | Raw TLS certificate |
| `TLS_CERT_PATH` | `/ssl/tls.crt` | Path to TLS certificate |
| `TLS_KEY` |  | Raw TLS key |
| `TLS_KEY_PATH` | `/ssl/tls.key` | Path to TLS key |
| `EXECUTION_TIMEOUT` | 300 | Timeout (seconds) for command execution |
| `KERBEROS_DEFAULT_REALM` | `EXAMPLE.COM` | The domain equivalent name of the Kerberos realm (all caps) |
| `KERBEROS_KDC_DOMAIN` | `kdc.example.com` | Full KDC domain name |
| `KERBEROS_ADMIN_SERVER` | `admin.example.com` | Full admin server domain name |
| `KERBEROS_PRINCIPAL_USER_NAME` | `admin` | Name of the principal for Kerberos auth |
| `KERBEROS_PRINCIPAL_USER_PASSWORD` | `PASSWORD` | Password of the principal for Kerberos auth |

### 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.

| ENV Variable Name | Default | Description |
| --- | --- | --- |
| `TINES_TUNNEL_SECRET` |  | Base64 encoded JSON with tines tunnel secret |
| `TUNNEL_ORIGIN_CERT` | `~/.cloudflared/cert.pem` | Cloudflared certificate |
| `TINES_TUNNEL_TO_COH_INGRESS_PORT` | 443 | 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 `PORT` or `TLS_PORT`, you should set `TINES_TUNNEL_TO_COH_INGRESS_PORT` accordingly. |

### 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](https://stream.mux.com/hHtVXDHCCe02LO65I00F8TnXIjhPkkrQvY/high.mp4)

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**. By default, when changes are published to the live version of the story, they will be automatically synced to all destinations, unless the destination has Auto-sync mode disabled.

![](https://www.datocms-assets.com/55802/1781518825-screenshot-2026-06-15-at-11-20-10.png)

When there are pending changes for a story in the destination and auto-sync is disabled, changes must be manually synced via the Sync button.

![](https://www.datocms-assets.com/55802/1781519043-screenshot-2026-06-15-at-11-22-37.png)

## 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/1781519076-screenshot-2026-06-15-at-11-08-29.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,
- which stories should be synced,
- and the syncing mode for the destination - by default, destinations are set to automatically sync.

![](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 be synced immediately and will continue to sync on an ongoing basis as the selected stories are updated - provided auto-sync mode is enabled. When auto-sync is disabled, stories will only sync when first added to the destination. This means that when setting up a new destination, stories will sync immediately, but updates to stories already in the destination will not be synced - only stories newly added to the destination will trigger a sync.

## 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/1781519353-screenshot-2026-06-15-at-11-29-00.png)

When you select an individual destination, you can edit and delete existing sync destinations, and also run a manual sync – handy for debugging problems such as networking errors.

![](https://www.datocms-assets.com/55802/1782213217-screenshot-2026-06-23-at-12-12-51.png)

When deleting a sync connection, there is a possibility to choose whether to keep or remove the synced stories on the destination tenant. Check “Keep synced stories in destination” to detach the stories from the sync source and preserve them as independent stories. If keeping the stories would exceed your destination tenant’s usage limits, they are automatically disabled. Leave it unchecked to delete all synced stories from the destination (the previous default behavior).

[Video](https://stream.mux.com/Hj18HZ3ESMRyGiRHTXU3Gm4wF5hhzJcz/high.mp4)

## 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, navigate to the story in the destination team and choose 'Detach from source' in the story toolbar.

![](https://www.datocms-assets.com/55802/1781519549-screenshot-2026-06-15-at-11-31-58.png)

## 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.

### Disable personal teams

A tenant-wide setting that hides personal teams across the organization. When it is enabled, every user works exclusively from shared teams: personal teams disappear from team pickers and URLs, stories in personal teams stop executing, and new users are provisioned onto a shared team instead.

Nothing is deleted. Personal teams and their contents remain in the tenant. The setting is fully reversible.

> **INFO:** The disable personal teams setting is available on select enterprise tenants. If the toggle does not appear on the Users settings page, contact your Tines account team.

## Personal teams in Tines

Each Tines user has a personal team by default. Personal teams are useful for experimentation, but some organizations need all automation to live on shared, role-controlled teams that tenant owners can govern and audit.

Disable personal teams addresses that need for governance without removing data or migrating content.

## What changes when personal teams are disabled

With the setting enabled:

- Personal teams are **hidden** from team pickers, the team list, sharing dropdowns, and navigation.
- Personal-team URLs (for example `/personal/...`) **redirect** to one of the user's shared teams (most recently visited / first joined as fallback).
- Stories owned by personal teams **cannot execute**. Webhooks return HTTP `422`. Send to Story actions fail with a clear error in the run log. New events cannot be created for those stories.
- The **Convert to story** action in Workbench is hidden.
- The **Team and personal team access** option is removed from sharing dropdowns. Existing sharing rows display as **Team access only**.
- The **guided onboarding tour** is skipped for new users. The onboarding form and helper panel still run.
- The **Personal team AI actions** toggle is hidden in AI settings.
- New users are added to a **shared team** through admin invite, JIT, REST, or SCIM. Users who cannot be placed on at least one shared team are rejected with a structured error. (see New user provisioning)

Only **tenant owners** can enable or disable this setting.

## Disabling personal teams

1. Navigate to **Users** (`/settings/users`).
2. Turn on the **Disable personal teams** toggle.
3. Review the **dependency scan** in the confirmation dialog (see below).
4. Resolve any blockers, choose how to handle Send to Story callers if needed, and confirm.

![](https://www.datocms-assets.com/55802/1781101935-docs_disable-toggle.png)

Both enabling and disabling the setting are recorded in the [audit log](https://www.tines.com/docs/audit-logs/).

> **INFO:**
> Re-enabling personal teams is immediate:
> 
> -   Personal teams reappear in every UI surface.
> -   Stories in personal teams can execute again.
> -   Stories that were bulk-disabled through **Disable caller stories** remain disabled until re-enabled manually from each story page.

## Dependency scan

Before personal teams are disabled, Tines scans the tenant and surfaces three areas of impact.

![](https://www.datocms-assets.com/55802/1781102532-docs_modal-happy.png)

### Records shared from personal teams

Counts of shared credentials, resources, and templates that live in personal teams but are referenced elsewhere in the tenant. These objects **keep working** after personal teams are disabled. Only tenant owners can edit them once personal teams are hidden.

### Users without a shared team

Users who belong only to a personal team. Personal teams cannot be disabled while any non-service-account user is in this state.

Add each listed user to at least one shared team from their [user settings page](https://www.tines.com/docs/user-administration/) before trying again. Open the list from **View** in the dialog.

### Send to Story into personal teams

Shared-team stories that call a story in a personal team through Send to Story. If these callers are left enabled, they fail once personal teams are disabled.

When callers exist, choose one of:

- **Disable caller stories** - Tines disables each calling story when personal teams are turned off. Stories can be re-enabled later from the story page.
- **Allow caller stories to fail** - Calling stories stay enabled. The next time one runs, the Send to Story action fails with a structured error.

Open the caller list from **View** in the dialog. Each row links to the calling story.

![](https://www.datocms-assets.com/55802/1781102394-docs_modal-sts-callout.png)

## SCIM landing team

If `SCIM` user provisioning is enabled, the confirmation dialog requires a **SCIM landing team** and **role**.

> **IMPORTANT:** SCIM creates users before IdP group memberships are applied. The landing team is where those users are placed until group sync runs. The team does not need to be assigned in the IdP, and users are not automatically removed from the landing team afterwards.

Personal and archived teams are not available as landing teams.

The same SCIM landing team and role can be reviewed or updated later under **Settings → Authentication → User provisioning** while personal teams remain disabled.

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

## New user provisioning

While personal teams are disabled, every provisioning path must place the new user on at least one shared team:

- **Admin invite / bulk invite: **Invite must include at least one shared team.
- **REST ****`POST /api/v1/admin/users`**** :  **Request must include a shared-team membership.
- **JIT first login: **User must map to at least one shared team through group sync.
- **SCIM: **User is placed on the SCIM landing team until group memberships arrive.

If provisioning would leave a user with only a personal team, the request is rejected with a structured error.

Learn more in [User provisioning](https://www.tines.com/docs/user-provisioning/) and [JIT](https://www.tines.com/docs/jit/).

## 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.

| [**Accessing the Docker Registry**](/docs/self-hosted/getting-started/accessing-the-docker-registry) | Learn how to authenticate with the Tines container registry and pull the required images for your deployment. |
| --- | --- |
| [**Verifying Tines Images**](/docs/self-hosted/getting-started/verifying-tines-images) | Verify the integrity and authenticity of Tines container images before deploying to your environment. |
| [**Helm Charts**](/docs/self-hosted/getting-started/helm-charts) | Configure and deploy Tines using our official Helm charts. |

#### 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
- A `StorageClass` in your cluster that supports `ReadWriteOnce` persistent volumes.
- Access to the Tines image registry — see next section

It is recommended that the `StorageClass` have a `reclaimPolicy` of `Retain` to prevent unintended volume deletions.

> **NOTE:** OpenShift support is included starting with tines Helm chart `42.0.1`. OpenShift is detected on helm operations automatically. OpenShift images support running in the `restricted-v2` SCC. If you need to run scripts, `tines-command-runner` is the exception to this due to its security model. There are instructions on how to support this image in the **Installing** section.

> **NOTE:** Upgrading from versions before v39.0.0 requires extra steps. Please see instructions in the **Upgrading** section.

---

## Tines image registry

Tines self-hosted artifacts are delivered as a set of Docker images hosted at `registry.tines.com` or in the case of OpenShift `oci.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:

```
# registry.tines.com
echo $TINES_REGISTRY_KEY | docker login registry.tines.com \
  --username <your-tenant-name> \
  --password-stdin
# oci.tines.com
echo $OCI_PASSWORD | docker login \
  --username <your-oci-username> \
  --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>
```

If creating a database internally on an OpenShift cluster, set `PGDATA` to a subdirectory of `/var/lib/postgresql/data` so it can create the directory with the correct permissions on first boot.

```
db:
  PGDATA: /var/lib/postgresql/data/userdata

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 `null` and will use the cluster default `StorageClass` — update `storageClassName` in your `values.yaml` if you do not have a default `StorageClass` set or wish to use another type of storage.

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.

The chart also supports the OpenShift Route resource if you wish to leverage the platforms built-in functionality for similar purposes as the Kubernetes Ingress.

#### 💡 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
```

#### Option C — External load balancer via OpenShift Route

```
nginx:
  enabled: false
  
route:
  enabled: true
  annotations: {}
  host: tines.your-company.com
  path: /
  tls:
    termination: edge
    key: |-
      -----BEGIN PRIVATE KEY-----
      [...]
      -----END PRIVATE KEY-----
    certificate: |-
      -----BEGIN CERTIFICATE-----
      [...]
      -----END CERTIFICATE-----
    caCertificate: |-
      -----BEGIN CERTIFICATE-----
      [...]
      -----END CERTIFICATE-----
  wildcardPolicy: None

```

---

## Installing

Once your `values.yaml` is ready, create a dedicated namespace:

```
kubectl create namespace tines
```

***OpenShift Installation:***

`tines-command-runner` requires a ServiceAccount with a `anyuid` SCC or equivalent binding. This is because the application uses `sudo` across multiple non-root users to enforce isolation gurantees. This can be done as follows:

```
cat <<EOF > tcr-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tcr-anyuid
  namespace: tines
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:openshift:scc:anyuid
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:openshift:scc:anyuid
subjects:
  - kind: ServiceAccount
    name: tcr-anyuid
    namespace: tines
EOF
kubectl apply -f tcr-sa.yaml -n tines
rm -f tcr-sa.yaml
```

The `values.yaml` file should also be updated to leverage the newly created ServiceAccount, there is a section in the stock chart values file that shows how:

```
tinesCommandRunner:
  # If running in OpenShift, tines-command-runner requires the anyuid SCC or equivalent.
  serviceAccountName: tcr-anyuid
```

---

The chart can now be installed:

```
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`.

[Video](https://stream.mux.com/g5ozgrScUE73pfS0200TUC00Zwp5Sz2MmY2/high.mp4)

### 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 |

¹ If you are running scripts using our [custom runtimes](https://www.tines.com/docs/actions/tools/run-script/custom-runtimes/) feature, use only one tines-command-runner container instead. Consult the guidelines in our tines-command-runner [configuration reference](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#multi-container-deployments) for more context.

### 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** |

¹ If you are running scripts using our [custom runtimes](https://www.tines.com/docs/actions/tools/run-script/custom-runtimes/) feature, you'll need to follow the additional guidelines in our tines-command-runner [configuration reference](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#multi-container-deployments).

### 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** |

¹ If you are running scripts using our [custom runtimes](https://www.tines.com/docs/actions/tools/run-script/custom-runtimes/) feature, you'll need to follow the additional guidelines in our tines-command-runner [configuration reference](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#multi-container-deployments).

### 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** |

¹ If you are running scripts using our [custom runtimes](https://www.tines.com/docs/actions/tools/run-script/custom-runtimes/) feature, you'll need to follow the additional guidelines in our tines-command-runner [configuration reference](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#multi-container-deployments).

### 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. Our Docker Compose configuration starting from v41.5.4 overrides this and rotates logs every 100 megabytes per service up to 10 times and compresses the files. These settings can be changed for all services in the **x-default-logging** node or per service in the **logging** node in the **docker-compose.yml** file.
> 
> 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.
> 
> Example adjustments:
> 
> \# Increasing size per file, more files for all services
> x-default-logging: &default-logging
>   # set driver to "journald" if you have global host level log rotation rules/log shipping.
>   driver: json-file
>   # remove options if not using "json-file" driver.
>   options:
>     max-size: "200m" # increase from 100m default
>     max-file: "20" # increase from 10 default
>     compress: "true"
> 
>  
> # tines-app service logging override
>   tines-app:
>   ...
>     logging:
>       driver: json-file
>       options:
>         max-size: "400m" # increase file size limit
>         max-file: "5" # decrease file count
>         compress: "true"

### 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 support running Run Script actions on your infrastructure for self-hosted customers and for cloud customers using tunnels. For general details on the Run Script action, refer to the documentation [here](https://www.tines.com/docs/actions/templates/run-python-script/). 

## Overview

Run Script actions are executed locally using `tines-command-runner`. 

Our deployment of tines-command-runner includes two containers:

1. `tines-command-runner`
2. `pypi-server`([https://github.com/pypiserver/pypiserver)](https://github.com/pypiserver/pypiserver)

The `tines-command-runner` container handles executing scripts.

Usage of `pypi-server` is **optional. **The `pypi-server` acts as a local python package index, making the installation of packages efficient and easily configurable. For details on configuring tines-command-runner, including available environment variables and options for configuring your own custom package index, see our [tines-command-runner configuration reference](https://www.tines.com/docs/run-script-tines-command-runner/).

### Recommended System Requirements

> **INFO:**
> -   2 vCPU
> -   1.5GB Memory
> -   10-20GB disk space
> -   For heavier workloads, you may need to allocate more 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 increased 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 in our Python image, listed in the configuration reference linked in the overview above.
> -   A private network. Tines-command-runner does not have a built-in authentication system, and is meant to be reachable from a Tines tenant only.
> -   **Note that dependencies will be installed multiple times if multiple teams or multiple distinct scripts are using the same dependencies. Three teams using the same dependencies will result in three copies of those dependencies being stored on disk by tines-command-runner.**

### 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 when running scripts for a particular team.

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

Use the below Docker Compose configuration as a starting point for your deployment of 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 configuring resource limits for tines-command-runner to ensure that run script execution doesn't impact other containers on your host. For example, to dedicate 2 of the host's CPU cores and 1GB of memory:

```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

Adapt the below commands to run tines-command-runner directly 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
```

#### 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 [Docker registry](http://tines.com/docs/self-hosted/getting-started/accessing-the-docker-registry/) for the `tines-app` image. 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 task count 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 support running Run Script actions on your infrastructure for self-hosted customers and for cloud customers using tunnels. For general details on the Run Script action, refer to the documentation [here](https://www.tines.com/docs/actions/templates/run-python-script/). 

## Overview

Run Script actions are executed locally using `tines-command-runner`. 

Our deployment of tines-command-runner includes two containers:

1. `tines-command-runner`
2. `pypi-server`([https://github.com/pypiserver/pypiserver)](https://github.com/pypiserver/pypiserver)

The `tines-command-runner` container handles executing scripts.

Usage of `pypi-server` is **optional. **The `pypi-server` acts as a local python package index, making the installation of packages efficient and easily configurable. For details on configuring tines-command-runner, including available environment variables and options for configuring your own custom package index, see our [tines-command-runner configuration reference](https://www.tines.com/docs/run-script-tines-command-runner/).

### Recommended System Requirements

> **INFO:**
> -   2 vCPU
> -   1.5GB Memory
> -   10-20GB disk space
> -   For heavier workloads, you may need to allocate more 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 increased 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 in our Python image, listed in the configuration reference linked in the overview above.
> -   A private network. Tines-command-runner does not have a built-in authentication system, and is meant to be reachable from a Tines tenant only.
> -   **Note that dependencies will be installed multiple times if multiple teams or multiple distinct scripts are using the same dependencies. Three teams using the same dependencies will result in three copies of those dependencies being stored on disk by tines-command-runner.**

### 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 when running scripts for a particular team.

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)

## Deployment

### Step 1. Prepare the tines-command-runner Docker image 

To simplify deployment, 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 

Deploy the `tines-command-runner` container alongside your 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 add 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. In Fargate, 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 and tines-app application.

For more information on tuning tines-command-runner for longer timeouts, see our [configuration reference](https://www.tines.com/docs/run-script-tines-command-runner/).

This should be done on your [.env file](/docs/self-hosted/deploying-tines/aws-fargate/deployment-guide/#step-7-create-a-env-file) or in your AWS task definition.

**.env file**

```bash
# Other tines-app env vars
RUN_SCRIPT_MAX_TIMEOUT=90
```

**AWS Fargate (add to the 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.

For documentation on environment variables for self-hosted run-script (tines-command-runner), consult our [run-script configuration](https://www.tines.com/docs/run-script-tines-command-runner/) guide instead.

```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_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 connection.
# SMTP_OPENSSL_CA_FILE=
# Alternatively, a path to a directory containing PEM-format CA certificates
# SMTP_OPENSSL_CA_PATH=

# 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_EMAILS=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.

# Workbench for Storyboard

In order to effectively use [Workbench for Storyboard](https://www.tines.com/docs/stories/workbench-for-storyboard/), we strongly recommended using the latest AI models from the major, foundational AI providers (Anthropic and OpenAI).

# Automatic mode

In order use [automatic mode](https://www.tines.com/docs/actions/types/event-transformation/automatic/), 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 just HTTP transport protocol out of the box. For advice on receiving traces via gRPC please reach out to the Tines support team.
- 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_ENDPOINT](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/)=<where to receive traces> Typically http://localhost:4318 (HTTP) is the default, 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)$")'

```

#### Run Script (tines-command-runner)

This page documents how to configure your tines-command-runner instance using environment variables. For platform-specific deployment instructions, consult our guides to deploying tines-command-runner with [AWS Fargate](https://www.tines.com/docs/run-script-setup/) or [Docker Compose](https://www.tines.com/docs/run-script-setup/), or use our [Helm charts](https://www.tines.com/docs/helm-charts/) to deploy to Kubernetes.

# Configuring tines-command-runner

Tines-command-runner exposes settings for controlling Run Script action execution using environment variables. See the platform-specific pages linked above for more on setting environment variables in tines-command-runner using your platform of choice.

## Basic settings

- `LOG_LEVEL` - Configures the logging level for the Python harness. Set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (default: `INFO`). Set `LOG_LEVEL=DEBUG` to enable verbose debug logging for troubleshooting. Logs are written to `/tmp/tcr-logs/harness-{environment_id}-{stdout,stderr}.log`.

## Package indexes

These settings can be changed to use your own, custom index for Python packages.

- `PIP_INDEX_URL` - Specifies the primary Python package index used for package resolution and download.                                    Default: `https://pypi.org/simple`.
- `PIP_EXTRA_INDEX_URL` - Specifies a fallback Python package index to be used if the primary index is offline or missing a desired package. Default: `http://pypi-server:8080/simple/`, a local pypi server that runs alongside your tines-command-runner container.
- `NO_PIP_INDEX` - If set to `true`, blocks access to all package indexes, even if they are specified by `PIP_INDEX_URL` or `PIP_EXTRA_INDEX_URL`, restricting dependencies to those bundled with the Python image (see "Pre-installed packages" below).
- `TRUSTED_HOST` - A hostname for a package index that can be accessed via unsecured HTTP when fetching packages. Default: `pypi-server`

### Pre-installed packages

The Python packages below are included in our bundled Python image by default. These packages are available even if `NO_PIP_INDEX=true`. More packages may be added to this list in the future, and the installed version of these packages may change.

These packages are **not **available by default to user-provided custom runtimes. Custom runtimes are fully self-contained and must bundle all required dependencies.

```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.

## Performance and scaling

### Timeouts

- `RUN_SCRIPT_MAX_TIMEOUT` - Controls the maximum runtime of scripts. Scripts running for longer than this number of seconds will be killed, and the task will be marked as a failure. Default: `60` (sixty seconds)

> **WARNING:**
> Increasing your script timeout has side effects and may not be the most effective solution for managing slow scripts.
> 
> #### 1\. Resource Requirements
> 
> Longer-running scripts typically are operating on larger data, which requires additional resources. Scale your configuration accordingly. For example, for a 300-second timeout, consider allocating at least 4GB memory and 4 CPU cores to tines-command-runner.
> 
> #### 2\. Concurrency
> 
> Long-running tasks occupy a worker as long as they're running, which means other tasks won't be able to run until they finish. If you anticipate needing to run many long-running tasks at once, you'll need to scale up accordingly. Consider increasing the number of tines-command-runner containers based on your concurrent script execution needs, or increasing the number of jobs each container can run in parallel. See the [parallel execution section](https://www.tines.com/docs/self-hosted/configuring-tines/run-script-tines-command-runner/#parallel-execution) below for more on increasing concurrency.
> 
> #### 3\. System Impact
> 
> To manage the impact of long-running tasks on your system:
> 
> -   Monitor CPU and memory usage in tines-command-runner
> -   Review if jobs are queueing or otherwise being blocked by long-running executions
> -   Increase resources and (potentially) parallel execution limits proportionally with timeout increases
> -   Set up resource utilization alerts to be notified of unexpectedly high utilization
>     -   High memory utilization is especially critical, as exhausting your memory will crash tines-command-runner and cause all running jobs to fail
> 
> #### 4\. Splitting out your workload to avoid timeouts
> 
> If your scripts are consistently running very slowly, rather than increasing your timeout, consider whether your script can be rewritten to handle **many small chunks** of the task it's performing in separate executions rather than performing the full task in a single execution. This can improve reliability and significantly reduce resource consumption.

### Parallel execution

By default, each run-script action on a container can run two tasks concurrently before further tasks begin to queue, and a tines-command-runner container can run up to ten concurrent tasks in total before tasks for any environment will begin to queue. You can increase the number of parallel executions by either adding more containers or increasing the parallel execution limit within the container. **Scaling containers vertically is recommended over scaling horizontally with multiple containers when possible**. Vertical scaling improves initial execution time for new actions, reduces required disk space, and can better handle high throughput with less risk of being overwhelmed by bursts of tasks consuming significant resources.

> **WARNING:** Running multiple tines-command-runner containers in parallel **requires special configuration** when running scripts with custom runtimes. Incorrect configuration of multi-container deployments can lead to dramatically slowed execution or unpredictable job failures. See "Multi-container deployments" below.

#### Configuration

- `TINES_COMMAND_RUNNER_PARALLEL_RUNS` controls the number of tasks a given action can run concurrently. Tasks for that action that are submitted while this many tasks are running will queue until a running task completes. Default: `2`
- `TINES_COMMAND_RUNNER_THREADS` controls the total number of tasks the tines-command-runner container can run at once. If more tasks come in while this many tasks are running, runs will begin to queue *regardless of whether the requested action has reached the limit set by* `TINES_COMMAND_RUNNER_PARALLEL_RUNS`. Default: `10`
- `TCR_ENABLE_PARALLEL_CUSTOM_ENVIRONMENTS` enables parallel execution for custom runtimes when set to `true`.  **Make sure your scripts run safely when running concurrently before enabling this. **When enabled, custom runtimes will be limited to the number of concurrent runs specified in `TINES_COMMAND_RUNNER_PARALLEL_RUNS`. Default: `false`

### Multi-container deployments

Tines-command-runner includes features that rely on information kept locally on a container. If you are running [run script ](https://www.tines.com/docs/run-script/)actions **that use **[**custom runtimes**](https://www.tines.com/docs/custom-runtimes/) and you plan to run **multiple replicas/pods/tasks **for tines-command-runner, you may need to take some additional steps to ensure custom runtimes work correctly.

If you are **not using custom runtimes**, or you are running **only one tines-command-runner instance**, there are no additional steps needed.

The steps below are broken down by deployment platform.

#### Kubernetes

If you're deploying using our [Helm charts](https://www.tines.com/docs/helm-charts/), **no further steps are needed**. Tines-command-runner is configured correctly out of the box.

If you're deploying using custom Kubernetes resources or in-house Helm charts, ensure that you have a Service configured for tines-command-runner that has `spec.sessionAffinity` set to `ClientIP`. The timeout can be tuned to your liking, but should be at least fifteen minutes.  If you are running a custom proxy that handles routing inbound traffic to tines-command-runner, you'll need to ensure that it behaves equivalently to the above session affinity setting (i.e. requests from the same tines-app pod are routed to the same tines-command-runner pod for 15 minutes or longer).

#### Docker Compose

Docker Compose does not support sticky sessions, so we recommend using only a single tines-command-runner task in your deployment with resources increased as needed to handle higher throughput. Use the environment variables in the Parallel Execution section above to tune the max parallel execution settings for your single instance of tines-command-runner.

If multiple tasks are required for your use case (e.g. for high-availability requirements), then you must configure a proxy to handle session management. We suggest using [Traefik Proxy](https://traefik.io/traefik), which has guidance on configuring sticky sessions [here](https://traefik.io/glossary/what-are-sticky-sessions).

#### AWS ECS

If you are following our recommended architecture and running tines-command-runner as a sidecar for your tines-sidekiq service, **no further steps are needed**.

If you are running a custom architecture and run tines-command-runner as a separate ECS service, ensure that you are using an application load balancer to route traffic to your tines-command-runner service, and update the settings for that load balancer to enable cookie stickiness (refer to the [AWS documentation](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html#enable-sticky-sessions-duration)), with an expiration period of at least 15 minutes (900 seconds).

#### 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 from `/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.

#### Upgrading Helm from charts before v39.0.0

# **Summary**

Tines Helm charts before v39.0.0 created the Kubernetes namespace as part of the chart. In those installs, Helm may own the namespace.

When upgrading to v39.0.0 or later, the chart no longer includes the namespace. If Helm owns your namespace, `helm upgrade`  can delete the namespace and the resources inside it, including PostgreSQL resources and persistent volumes. If you self-host Tines with Helm and installed a chart before v39.0.0, complete the checks below before running `helm upgrade.`

# **Check whether you’re impacted**

You may be impacted if Helm created your namespace and you have not yet upgraded to v39.0.0 or later.
Replace `<ns>` with your Tines namespace:

```
kubectl get namespace <ns> -o jsonpath='managed-by={.metadata.labels.app\.kubernetes\.io/managed-by}{"  "}release={.metadata.annotations.meta\.helm\.sh/release-name}{"\n"}'
```

If this prints `managed-by=Helm` with your release name, you may be impacted. You’re likely not impacted if you created the namespace yourself, for example with `--create-namespace` or `kubectl create namespace`.

# **Choose the right upgrade path**

Next, check whether your Helm release namespace matches the namespace where Tines is running:

```
helm list -A | grep -i tines          # NAMESPACE column = your release namespace
kubectl get pods -A | grep tines-app  # first column = where Tines is running
```

- If they match (for example, `helm install tines <chart> -n tines --create-namespace`), use Path A — in-place upgrade.
- If they differ, use Path B — migration required. The namespace annotation alone will not protect your data in this layout.

## **Path A — in-place upgrade**

1. Annotate the namespace so Helm does not delete it during the upgrade:

```
kubectl annotate namespace <ns> helm.sh/resource-policy=keep --overwrite
```

2. Confirm the destination storage is available and points to the data you expect. For bundled PostgreSQL (`db.create: true`, the default), confirm the database PVC exists:

```
kubectl get pvc -n <ns>
```

Then check free space on the PostgreSQL data volume:

```
kubectl exec -n <ns> deploy/db -c postgres -- \df -h /var/lib/postgresql/data
```

If you use an external or managed PostgreSQL database (`db.create: false`), confirm available storage using your database provider’s tools before upgrading.

3. Back up PostgreSQL off-cluster before upgrading:

```
kubectl exec -n <ns> deploy/db -- pg_dump -U <DATABASE_USERNAME> -d tines_production > tines-backup-$(date +%F).sql
```

4. Upgrade using `helm upgrade`  with your existing values and flags.
5. Verify the namespace is still active and is no longer included in the Helm manifest:

```
kubectl get namespace <ns>
helm get manifest <release> -n <ns> | grep -c "kind: Namespace"
```

The second command should print `0`.

## **Path B — migration required**

If your Helm release namespace and Tines runtime namespace differ, v39.0.0 or later may deploy workloads into a different namespace and create a new, empty database. In this case:

1. Back up PostgreSQL off-cluster.
2. Install v39.0.0 or later into the intended namespace.
3. Restore the PostgreSQL backup into the new database.
4. Verify Tines starts with the restored data before removing the old namespace.
  

> **NOTE:** `helm.sh/resource-policy=keep` protects only the namespace object. If you later run `helm uninstall` , Helm may still delete the resources it manages inside that namespace. The namespace itself will remain and can be removed manually if needed

### 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

Use this page to find the right diagnostic steps for your self-hosted Tines deployment. Start with the in-app tools that work on every deployed platform, then follow the guide for how you deployed Tines to collect logs and configuration details.

## Before you contact support

Include as much of the following as you can in your support request:

- **What is happening** — symptoms, error messages, and when the issue started
- **What changed recently** — upgrades, configuration changes, network or infrastructure changes
- **Tines version** — application version; for Kubernetes, also include the Helm chart version
- **Deployment platform** — for example Docker Compose on a VM, Amazon EKS, Google Kubernetes Engine (GKE), or AWS Fargate.
- **Deployment Architecture** 
  
  - **Container Instances:** How many instances of each Tines container are currently running in the deployment?
  - **Database Architecture:** Is the database hosted externally, or does it run internally within your deployment platform?
  - **Resource Allocation:** What are the specific CPU and memory allocations designated for your containers?
- **HAR file **— ** **HTTP requests from your web browser. [See Generate a HAR file for troubleshooting](https://explained.tines.com/en/articles/8397915-generate-a-har-file-for-troubleshooting)
- **Steps you already tried** — restarts, health checks, and any diagnostic output you collected

Review all output before you share it. Logs and diagnostic bundles can contain passwords, tokens, or other sensitive data.

## Start here: in-app diagnostics

These tools work regardless of how you host Tines.

### System diagnostics

If you are on Tines v41.1 or later, open **Settings → Self-hosted → System diagnostics** to run health checks and debug tools from inside the application. 

See [System diagnostics](https://www.tines.com/docs/self-hosted/troubleshooting/system-diagnostics/) for full instructions.

### Tenant health dashboard

Admins can open **Tenant health** in the main navigation for a live view of container versions, database and Redis health, and background job queues. Use this to spot version mismatches, connectivity problems, or stuck jobs before you dig into logs.

See [Tenant health dashboard](https://www.tines.com/docs/self-hosted/monitoring-tines/tenant-health-dashboard/).

## Collect diagnostics by platform

Choose the guide that matches how you deployed Tines.

### Docker Compose

Use this path if you installed Tines with Docker Compose on a virtual machine or bare-metal host.

See [Troubleshooting Docker Compose](https://www.tines.com/docs/self-hosted/troubleshooting/troubleshooting-tines-on-docker-compose/) for troubleshooting scripts, restart steps, log collection, and what to send support.

### Kubernetes

Use this path if you deployed Tines with Helm on a Kubernetes cluster (for example EKS, GKE, AKS, or OpenShift).

See [Troubleshooting Kubernetes](https://www.tines.com/docs/self-hosted/troubleshooting/troubleshooting-kubernetes/) for the diagnostics collector, what is collected, and manual collection commands.

### AWS Fargate

Use this path if you deployed Tines on Amazon ECS with Fargate.

See [Troubleshooting AWS Fargate](https://www.tines.com/docs/self-hosted/troubleshooting/troubleshooting-aws-fargate-deployments/) for networking checks, common symptoms, and links to [log collection](https://www.tines.com/docs/self-hosted/troubleshooting/troubleshooting-aws-fargate-deployments/log-collection-on-aws-fargate/) and [restarting services](https://www.tines.com/docs/self-hosted/troubleshooting/troubleshooting-aws-fargate-deployments/restarting-tines-on-aws-fargate/).

### Other container platforms

1. Run **System diagnostics** and review the **Tenant health** dashboard.
2. Collect logs from your application and worker containers using your orchestrator's standard log tools.
3. Include your Tines version, platform name, and a description of the issue when you contact support.

See [Before you begin](https://www.tines.com/docs/self-hosted/) for compatible environments.

#### System diagnostics

System diagnostics helps self-hosted tenant admins troubleshoot a Tines installation from inside the application.

Use system diagnostics to run health checks, investigate common configuration issues, inspect key subsystems, and collect information to share with your infrastructure team or Tines support.

Requires Tines `v41.1` or later.

## Who can use system diagnostics

System diagnostics is available to tenant admins on self-hosted Tines deployments.

## Open system diagnostics

To open system diagnostics:

1. Sign in as a tenant admin.
2. Go to **Settings**.
3. Under **Self-hosted**, select **System diagnostics**.

System diagnostics has two tabs:

- **Troubleshoot**: Run health checks and debug tools.
- **Configuration**: Review active self-hosted configuration values.

![](https://www.datocms-assets.com/55802/1779893133-screenshot-2026-05-27-at-15-44-42.png)

## Run health checks

The **Troubleshoot** tab includes health checks for core parts of your Tines installation.

You can use these checks to verify:

- The Tines application container is running and responding.
- PostgreSQL, Redis, and Sidekiq are reachable.
- The database has the required permissions, extensions, and session settings.
- DNS resolves for important hosts, including your application domain, SMTP, PostgreSQL, and Redis.
- The installation can connect to Tines HQ for telemetry. This connection is optional and is not required for Tines to function.
- The largest PostgreSQL tables, to help identify storage-heavy tables.

Each check shows a status after it runs:

- **Success** means the check completed without finding an issue.
- **Error** means the check found a problem or could not complete.
- **Not run** means the check has not been run yet.
- **Running** means the check is currently in progress.

## Use debug tools

The **Troubleshoot** tab also includes debug tools for specific areas of the application.

Available tools can help you inspect:

- Users
- Stories and actions
- Cases
- Records
- SMTP configuration and email delivery
- DNS lookups
- Database status and pending migrations
- SSO certificates and TLS certificate chains

Some tools run immediately. Others ask for input first, such as an email address, hostname, port, certificate, or private key.

Only run tools that are relevant to the issue you are investigating. Some outputs may include operational details about your tenant, configuration, or infrastructure.

## Review configuration

The **Configuration** tab shows active self-hosted environment configuration values that are safe to display in the app.

Sensitive values, such as passwords, tokens, secrets, credentials, and private keys, are obscured before they are shown.

From this tab, you can:

- Load the current configuration.
- Copy the configuration as JSON.
- Download the configuration as a JSON file.

Before sharing downloaded or copied configuration, review it to make sure it is appropriate for the recipient.

## Share diagnostics with support

When working with Tines support, you may be asked to run specific checks or tools and share the output.

To share a result:

1. Run the relevant health check or debug tool.
2. Copy the output from the result.
3. Review the output for sensitive or environment-specific information.
4. Share it through your approved support channel.

For configuration details, use **Copy as JSON** or **Download as JSON** from the **Configuration** tab, then review the file before sharing.

## Troubleshooting access

If you do not see **System diagnostics** in settings:

- Confirm that you are signed in as a tenant admin.
- Confirm that you are using a self-hosted Tines deployment.
- Confirm that your Tines version supports system diagnostics.
- Contact Tines support if the page should be available but is not visible.

#### 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
# Get the last 500 lines of logs for a container.
sudo docker logs --tail 500 (container id) > tines.log
# Get the last 2 hours of logs for a container.
sudo docker logs --since 2h (container id) > tines.log
# Get logs between 2 UTC times for a container.
sudo docker logs --since "2026-07-17T10:00:00Z" --until "2026-07-17T11:00:00Z" (container id) > tines.log
# Follow/stream container logs live.
sudo docker logs --follow (container id)
# Get all logs for a container.
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 Kubernetes

We provide a read-only script `k8s-diagnostics-collector.sh` that collects Kubernetes logs, events, and API objects from a single namespace. It packages everything into a `.tar.gz` bundle you can send to Tines support to help diagnose issues with your deployment (for example pods that restart, crash, or fail health checks).

The bundle includes kubectl context and version information, pod status, resource usage, namespace events, container logs, and namespaced Kubernetes objects. Secrets and ConfigMaps are excluded because they may contain credentials or sensitive configuration.

When you contact support, include:

- **Tines version** — the Helm chart version and/or application version you have deployed
- **Kubernetes platform** — where the cluster runs, for example Google Kubernetes Engine (GKE), Amazon EKS, Microsoft Azure Kubernetes Service (AKS), or another distribution

Review the bundle and **remove sensitive information** before sending it to support. Container logs and exported objects may contain sensitive data you do not want to share. Pay close attention to app secrets, passwords, and tokens.

## Quickstart

You need `kubectl` configured for the target cluster. Install it from the [Kubernetes documentation](https://kubernetes.io/docs/tasks/tools/) if it is not already on your PATH.

Download the script, make it executable, and run it against your Tines namespace:

```bash
curl -fsSL -O https://assets.sh.tines.com/collectors/k8s/k8s-diagnostics-collector.sh
chmod +x k8s-diagnostics-collector.sh
./k8s-diagnostics-collector.sh --namespace your-tines-namespace
```

The script prints the current kubectl context and asks you to confirm before collecting any data. When it finishes, it creates an archive named `k8s-namespace-debug-<namespace>-<timestamp>.tar.gz`.

Send that archive to your Tines support contact after reviewing its contents and removing anything sensitive.

---

## Manual collection

This section walks through the same read-only diagnostic collection performed by `k8s-diagnostics-collector.sh`, but as individual commands you can run yourself. Use this when you prefer not to run the script, need to collect only part of the bundle, or want to inspect output as you go.

The automated script is equivalent to running every section below in order and packaging the result into a `.tar.gz` archive.

## What is collected

- Current kubectl context and client/server version
- Pod status for all pods in the namespace
- Current CPU and memory usage for all pods (`kubectl top`; requires metrics-server)
- Namespace events (probe failures, OOMKills, scheduling, and so on)
- Container logs for every pod (current and previous for regular containers; current for init containers)
- All namespaced Kubernetes API objects **except Secrets and ConfigMaps**

## What is not collected

- Secrets (may contain credentials)
- ConfigMaps (may contain sensitive application configuration)
- Cluster-wide resources outside the target namespace
- Historical metrics or live profiling data

## Prerequisites

- `kubectl` installed and configured for the target cluster
- Permission to read pods, logs, events, and other namespace resources in the target namespace
- `bash`, `date`, `mkdir`, `tar`, and standard Unix utilities on your PATH

---

## 1. Set environment variables

Define these once at the start of your shell session. Every section below reuses them.

```bash
# Required: namespace to collect from
NAMESPACE="your-namespace"

# Optional: max log lines per container (default in the script is 5000)
LOG_TAIL=5000

# Output directory and archive names (timestamp keeps each run unique)
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
OUT_DIR="k8s-namespace-debug-${NAMESPACE}-${TIMESTAMP}"
ARCHIVE="${OUT_DIR}.tar.gz"
```

---

## 2. Confirm kubectl context

**Purpose:** Verify you are connected to the intended cluster before collecting any data.

```bash
kubectl config current-context
```

Review the output. Switch context if needed:

```bash
kubectl config use-context <context-name>
```

Optionally store the context name for your records:

```bash
CONTEXT="$(kubectl config current-context)"
echo "Using context: ${CONTEXT}"
```

---

## 3. Verify the namespace exists

**Purpose:** Fail fast if the namespace name is wrong or you lack access.

```bash
kubectl get namespace "${NAMESPACE}"
```

---

## 4. Create the output directory

**Purpose:** Prepare the folder layout used by the script (`logs/` and `objects/` subdirectories).

```bash
mkdir -p "${OUT_DIR}/logs" "${OUT_DIR}/objects"
```

---

## 5. Write collection metadata

**Purpose:** Record when the bundle was collected and which options were used.

```bash
{
  echo "collected_at=${TIMESTAMP}"
  echo "namespace=${NAMESPACE}"
  echo "log_tail=${LOG_TAIL}"
} > "${OUT_DIR}/metadata.txt"
```

---

## 6. Save kubectl context

**Purpose:** Capture which cluster/context the data came from.

**Command:** `kubectl config current-context`

**Output file:** `${OUT_DIR}/context.txt`

```bash
kubectl config current-context > "${OUT_DIR}/context.txt" 2>&1
```

---

## 7. Save kubectl client and server version

**Purpose:** Document kubectl and API server versions for compatibility troubleshooting.

**Command:** `kubectl version --output=yaml`

**Output file:** `${OUT_DIR}/kubectl-version.yaml`

```bash
kubectl version --output=yaml > "${OUT_DIR}/kubectl-version.yaml" 2>&1
```

---

## 8. Save pod status

**Purpose:** Snapshot pod names, phases, nodes, restart counts, and IP addresses.

**Command:** `kubectl get pods -o wide`

**Output file:** `${OUT_DIR}/pods.txt`

```bash
kubectl -n "${NAMESPACE}" get pods -o wide > "${OUT_DIR}/pods.txt" 2>&1
```

---

## 9. Save pod resource usage

**Purpose:** Capture current CPU and memory usage for every pod in the namespace. Requires metrics-server in the cluster; the command fails harmlessly if it is not installed.

**Command:** `kubectl top pods`

**Output file:** `${OUT_DIR}/pods-top.txt`

```bash
kubectl -n "${NAMESPACE}" top pods > "${OUT_DIR}/pods-top.txt" 2>&1
```

---

## 10. Save namespace events

**Purpose:** Collect scheduling, probe failure, OOMKill, and other cluster events for the namespace, ordered by time.

**Command:** `kubectl get events --sort-by=.lastTimestamp`

**Output file:** `${OUT_DIR}/events.txt`

```bash
kubectl -n "${NAMESPACE}" get events --sort-by=.lastTimestamp \
  > "${OUT_DIR}/events.txt" 2>&1
```

---

## 11. Save namespace API objects (excluding Secrets and ConfigMaps)

**Purpose:** Export YAML for every namespaced resource type the API exposes, except Secrets and ConfigMaps.

**Command:** For each namespaced resource type, `kubectl get <resource> -o yaml`

**Output directory:** `${OUT_DIR}/objects/` (one file per resource type)

```bash
for RESOURCE in $(kubectl api-resources --namespaced=true --verbs=list -o name); do
  case "${RESOURCE}" in
    secrets|configmaps)
      continue
      ;;
  esac
  SAFE_NAME="${RESOURCE//\//_}"
  echo "Collecting ${RESOURCE} -> objects/${SAFE_NAME}.yaml"
  kubectl -n "${NAMESPACE}" get "${RESOURCE}" -o yaml \
    > "${OUT_DIR}/objects/${SAFE_NAME}.yaml" 2>&1
done
```

To collect a single resource type instead of everything, run one command directly. Example for Deployments:

```bash
kubectl -n "${NAMESPACE}" get deployments.apps -o yaml \
  > "${OUT_DIR}/objects/deployments.apps.yaml" 2>&1
```

---

## 12. Save container logs

**Purpose:** Capture application output from every pod. Regular containers get both current logs and previous (crashed) logs; init containers get current logs only. Each file is capped at `${LOG_TAIL}` lines.

### 12a. Regular containers — current logs

**Command:** `kubectl logs <pod> -c <container> --tail=<lines>`

**Output file pattern:** `${OUT_DIR}/logs/<pod>__<container>__current.log`

```bash
for pod in $(kubectl -n "${NAMESPACE}" get pods -o name); do
  POD="${pod#pod/}"
  for CONTAINER in $(kubectl -n "${NAMESPACE}" get pod "${POD}" \
    -o jsonpath='{.spec.containers[*].name}'); do
    kubectl -n "${NAMESPACE}" logs "${POD}" -c "${CONTAINER}" \
      --tail="${LOG_TAIL}" \
      > "${OUT_DIR}/logs/${POD}__${CONTAINER}__current.log" 2>&1
  done
done
```

### 12b. Regular containers — previous (crashed) logs

**Command:** `kubectl logs <pod> -c <container> --previous --tail=<lines>`

**Output file pattern:** `${OUT_DIR}/logs/<pod>__<container>__previous.log`

No previous log exists if the container has never restarted; that command will fail harmlessly for those containers.

```bash
for pod in $(kubectl -n "${NAMESPACE}" get pods -o name); do
  POD="${pod#pod/}"
  for CONTAINER in $(kubectl -n "${NAMESPACE}" get pod "${POD}" \
    -o jsonpath='{.spec.containers[*].name}'); do
    kubectl -n "${NAMESPACE}" logs "${POD}" -c "${CONTAINER}" --previous \
      --tail="${LOG_TAIL}" \
      > "${OUT_DIR}/logs/${POD}__${CONTAINER}__previous.log" 2>&1
  done
done
```

### 12c. Init containers — current logs

**Command:** `kubectl logs <pod> -c <init-container> --tail=<lines>`

**Output file pattern:** `${OUT_DIR}/logs/<pod>__init-<init-container>__current.log`

```bash
for pod in $(kubectl -n "${NAMESPACE}" get pods -o name); do
  POD="${pod#pod/}"
  for INIT_CONTAINER in $(kubectl -n "${NAMESPACE}" get pod "${POD}" \
    -o jsonpath='{.spec.initContainers[*].name}'); do
    kubectl -n "${NAMESPACE}" logs "${POD}" -c "${INIT_CONTAINER}" \
      --tail="${LOG_TAIL}" \
      > "${OUT_DIR}/logs/${POD}__init-${INIT_CONTAINER}__current.log" 2>&1
  done
done
```

### 12d. Single pod or container

To collect logs for one pod only, set `POD` and run the relevant command directly:

```bash
POD="my-pod-name"
CONTAINER="my-container"

kubectl -n "${NAMESPACE}" logs "${POD}" -c "${CONTAINER}" \
  --tail="${LOG_TAIL}" \
  > "${OUT_DIR}/logs/${POD}__${CONTAINER}__current.log" 2>&1
```

---

## 13. Create the archive

**Purpose:** Package the output directory into a single file for sharing with support, matching the script's deliverable.

**Command:** `tar -czf`

**Output file:** `${ARCHIVE}`

```bash
tar -czf "${ARCHIVE}" "${OUT_DIR}"
echo "Created archive: ${ARCHIVE}"
```

---

## Before sharing

Container logs and exported objects may contain sensitive application data. Review the contents of `${OUT_DIR}` or `${ARCHIVE}` and remove anything you do not want to share before sending the bundle to Tines support.

When you open a support request, include your **Tines version** (Helm chart and/or application version) and **Kubernetes platform** (for example GKE, EKS, or AKS).

#### 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.

### **July 21st, 2026 - 42.2.0**

Release Details (JSON) 9867eb3d_v42_2_0 - [https://assets.sh.tines.com/releases/42.2.0-rd.json](https://assets.sh.tines.com/releases/42.2.0-rd.json)

- [Restrict who can create API keys](https://www.tines.com/whats-new/restrict-who-can-create-api-keys)
- [Create cases from a template via the API](https://www.tines.com/whats-new/create-cases-from-a-template-via-the-api)

### **
July 14th, 2026 - 42.1.0**

Release Details (JSON) 907b5305_v42_1_0 - [https://assets.sh.tines.com/releases/42.1.0-rd.json](https://assets.sh.tines.com/releases/42.1.0-rd.json)

- [Case activity panel refresh](https://www.tines.com/whats-new/improved-case-activity-ui)
- [GPT-5.6 models are now available in Tines](https://www.tines.com/whats-new/gpt-56-models-are-now-available-in-tines)
- [Target test or live data with non-read record actions](https://www.tines.com/whats-new/record-data-mode-test-live)
- [Story change control requirements](https://www.tines.com/whats-new/story-change-control-requirements)

### **
July 7th, 2026 - 42.0.0**

Release Details (JSON) 6d0294aa_v42_0_0 - [https://assets.sh.tines.com/releases/42.0.0-rd.json
](https://assets.sh.tines.com/releases/42.0.0-rd.json)

- [Notify story owners about a credential](https://www.tines.com/whats-new/notify-story-owners-about-a-credential)
- [Include references in the Credentials API](https://www.tines.com/whats-new/include-references-in-the-credentials-api)
- [User read and manage tenant permissions](https://www.tines.com/whats-new/user-read-and-manage-tenant-permissions)
- [Query and aggregate records from the storyboard](https://www.tines.com/whats-new/query-and-aggregate-records-directly-from-the-storyboard)
- [Increased limits for large text fields](https://www.tines.com/whats-new/increased-limits-record-large-text-field)
- Records storage migration:
  This release finalizes an internal migration of Records storage. For most customers no action is needed; the migration should already have completed automatically in the background while v41 was running.
  
  - If the tenant is not ready for upgrade to this version, a banner shows up in the UI.
  - The upgrade.sh script checks that the migration has finished and will safely cancel the upgrade if it hasn't. If that happens, stay on your current version until the in-app migration warning banner clears, then upgrade again.
  - If an instance is upgraded before the migration completes, admins will see an error banner in the app; contact Tines support before taking any further action.

### **June 30th, 2026 - 41.6.0**

Release Details (JSON) f5cc910d_v41_6_0 - [https://assets.sh.tines.com/releases/41.6.0-rd.json](https://assets.sh.tines.com/releases/41.6.0-rd.json)

- [Clean up synced stories when deleting a sync connection](https://www.tines.com/whats-new/clean-up-synced-stories-when-deleting-a-sync-connection)
- [Search text field contents of a record](https://www.tines.com/whats-new/search-record-content)
- [Story duplication](https://www.tines.com/whats-new/story-duplication)
- [Dashboard series limit and "other" grouping](https://www.tines.com/whats-new/dashboard-series-other-grouping)
- [Tines MCP Server](https://www.tines.com/whats-new/the-tines-mcp-server/)

### ~~**June 23rd, 2026 - 41.5.0**~~** [**DEPRECATED IN FAVOR OF 41.6.0]

Release Details (JSON) 6894b339_v41_5_0 - [https://assets.sh.tines.com/releases/41.5.0-rd.json](https://assets.sh.tines.com/releases/41.5.0-rd.json)

- [Folders for record types](https://www.tines.com/whats-new/folders-for-record-types)
- [Story syncing API improvements](https://www.tines.com/whats-new/story-syncing-api-improvements)
- [Manual sync mode for Story Syncing](https://www.tines.com/whats-new/manual-sync-mode-for-story-syncing)

### ~~**June 16th, 2026 - 41.4.0**~~** [**DEPRECATED IN FAVOR OF 41.6.0]

Release Details (JSON) 1035bbc0_v41_4_0 - [https://assets.sh.tines.com/releases/41.4.0-rd.json](https://assets.sh.tines.com/releases/41.4.0-rd.json)

- [Tunnel support for Anthropic and Bedrock](https://www.tines.com/whats-new/tunnel-support-for-anthropic-and-bedrock)
- [Resolve mentions in the Cases API](https://www.tines.com/whats-new/resolve-mentions-in-the-cases-api)
- [Case status API](https://www.tines.com/whats-new/case-status-api)
- [Workbench for Storyboard has moved](https://www.tines.com/whats-new/workbench-for-storyboard-has-moved)
- [Tenant-wide control over personal teams access](https://www.tines.com/whats-new/tenant-wide-control-over-personal-teams-access)
- [Lock sections in place](https://www.tines.com/whats-new/lock-sections-in-place)
- [Claude Fable 5 support via custom Bedrock and Anthropic providers](https://www.tines.com/whats-new/claude-fable-5-support-via-custom-bedrock-and-anthropic-providers)
- [OAuth Passthrough for MCP Servers](https://www.tines.com/whats-new/oauth-passthrough-for-mcp-servers)
- [Resolve Record artifacts in list mode](https://www.tines.com/whats-new/resolve-record-artifacts-in-list-mode)

### ~~June 9th, 2026 - 41.3.0~~ [DEPRECATED IN FAVOR OF 41.6.0]

Release Details (JSON) 9108a14c_v41_3_0 - [https://assets.sh.tines.com/releases/41.3.0-rd.json](https://assets.sh.tines.com/releases/41.3.0-rd.json)

- [Charting improvements roundup](https://www.tines.com/whats-new/charting-improvements-roundup)
- [Control chart legend visibility](https://www.tines.com/whats-new/control-chart-legend-visibility)
- [Accessible color styles for charts](https://www.tines.com/whats-new/accessible-color-styles-for-charts)
- [Record type schema explorer](https://www.tines.com/whats-new/record-type-schema-explorer)
- [Workbench skills](https://www.tines.com/whats-new/workbench-skills)
- [Track AI usage and costs via the API](https://www.tines.com/whats-new/ai-usages-api-endpoint-and-updated-workbench-api)
- [Templates in quick search](https://www.tines.com/whats-new/templates-in-quick-search)
- [Bring your own stack](https://www.tines.com/whats-new/bring-your-own-stack)
- [Share record types across teams](https://www.tines.com/whats-new/share-record-types-across-teams)

### June 2nd, 2026 - 41.2.0

Release Details (JSON) 34f5e598_v41_2_0 - [https://assets.sh.tines.com/releases/41.2.0-rd.json](https://assets.sh.tines.com/releases/41.2.0-rd.json)

- [Story copilot is now Workbench](https://www.tines.com/whats-new/workbench-for-stories)
- [Quick switcher for record type reports](https://www.tines.com/whats-new/quick-switcher-for-record-type-reports)
- [Story toolbar](https://www.tines.com/whats-new/story-toolbar)
- [Templates in toolbar quicksearch](https://www.tines.com/whats-new/templates-in-quick-search/)

### May 26th, 2026 - 41.1.0

```
Image digests for build 75fc541b_v41_1_0:
tines/tines-nginx:75fc541b_v41_1_0 sha256:bd3d2e3646040053027d3786e54b601fe31be249b0cf09b03b2732bc50d5ad18
tines/tines-nginx-unprivileged:75fc541b_v41_1_0 sha256:ba16550d10130ca7363da02d52fa6cfe383e635a1dd2953df7aaafa52d1aa7c4
tines/tines-app:75fc541b_v41_1_0 sha256:428af69e9a8d7638831c0c2e5932862707d0efb27d490a9a64fce1c5c7845059
tines/tines-app-with-fips:75fc541b_v41_1_0 sha256:8447c48ae294ec32174a57793c4f75d7cdb9bd25395d9434190aeceb987ecfa3
tines/tines-command-runner:75fc541b_v41_1_0 sha256:616fa12a96a846944337684f08cb19acf89d9e19af116038872642f0471ea03a
tines/tines-command-runner-fips:75fc541b_v41_1_0 sha256:517d50da7a50602b4bfa372588ce511f29e77cf1c7a5ece588d3e1c68ca2acb6
```

- [Multi-team access control for MCP servers](https://www.tines.com/whats-new/multi-team-access-control-for-mcp-servers)
- [Template folders](https://www.tines.com/whats-new/template-folders)

### 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)
- Note: `GET /api/v1/records/:id` no longer returns `child_record_ids`. Use `child_records` instead.

### 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)
- Note: `GET /api/v1/records/:id` no longer returns `child_record_ids`. Use `child_records` instead.

### 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](https://stream.mux.com/Caw6TKxODI00ymLWoc9mXXgGFwEFK1uxg/high.mp4)

#### 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)

[Video](https://stream.mux.com/io5QjfcZVYEQpOFpm322d3glr7xIoCDB/high.mp4)

#### 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.

[Video](https://stream.mux.com/rAc9eoB5gBCWY00hfwgQc7r00khUG01rzFX/high.mp4)

#### Create credentials from the storyboard

You can now create credentials from the storyboard.

[Video](https://stream.mux.com/gifsF00OAqXSwrRZP1K4tluszYOCYFTVb/high.mp4)

#### Copy event key path as pill

Double click on a key in an event payload to copy its path as a pill.

[Video](https://stream.mux.com/xssSywhHRxh3oF85OyRTRGd4ctAghpjM/high.mp4)

### 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

[Video](https://stream.mux.com/52DJVyJU7Z7fvbbsixF9902pCkkoqn8Zs/high.mp4)

#### 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.

[Video](https://stream.mux.com/iQe7VzpGJ4chZqxabQu3iQ5mp9wPTfv2/high.mp4)

#### 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.

[Video](https://stream.mux.com/afEVl8llUWgcm6QenFeG8sATm01LTUMKP/medium.mp4)

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

*Video*

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.

[Video](https://stream.mux.com/8tWmACH9KpklxmCmcscfHrJVsmRPp88q/high.mp4)

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.

[Video](https://stream.mux.com/FDJd8A8NLlGzIe7eezveWFen6LDLRXoa/high.mp4)

- 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.

[Video](https://stream.mux.com/iJp9ZNV11g009th2RIaJO4NnFOQq2F00pL/high.mp4)

- 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:

[Video](https://stream.mux.com/31HWP02uku02jDPWFFoEQjyR6W7SmgSyHs/high.mp4)

- 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.

[Video](https://stream.mux.com/CL00cNHPxBz2xk9LEcczex4XeLcAV2qtj/high.mp4)

- 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

[Video](https://stream.mux.com/00abjKRgwbx34d2PmnaInP7QXQa00zGFYL/high.mp4)

– Undo/Redo directly from a toast

[Video](https://stream.mux.com/8DTWAetdMKXrc02dioZXSRrDz9ZeGXY94/high.mp4)

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

[Video](https://stream.mux.com/c3IxKzzFRiVNaIOAIF97TXLg7I6dcrlC/high.mp4)

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
```

### Nightly releases

**Continuous improvements, every single night**

At Tines, we believe that building a great platform means never standing still. That's why we are rolling out **Nightly releases** to use in your environment!

Our nightly releases focus on the crucial details that keep your workflows moving flawlessly. They deliver:

-   **Minor fixes:** Swiftly resolving edge cases, interface bugs, and minor glitches so your automation experience remains frictionless.
-   **Performance optimizations:** Under-the-hood tweaks and enhancements that keep Tines running fast, lightweight, and efficient.
-   **Incremental stability:** Ensuring that small improvements are continuously integrated, making Tines better, safer, and more reliable every single day.

For self-hosted teams looking to track our broader release cadence and stable milestones, you can explore the full breakdown on our [Tines release notes](https://www.tines.com/docs/self-hosted/release-notes/) page.

### **July 21st, 2026 - 42.1.4**

Release Details (JSON) 126e1283\_v42\_1\_4 - [https://assets.sh.tines.com/releases/42.1.4-rd.json](https://assets.sh.tines.com/releases/42.1.4-rd.json)

### **July 18th, 2026 - 42.1.3**

Release Details (JSON) 69a132a8\_v42\_1\_3 - [https://assets.sh.tines.com/releases/42.1.3-rd.json](https://assets.sh.tines.com/releases/42.1.3-rd.json)

### **July 17th, 2026 - 42.1.2**

Release Details (JSON) f4efdc63\_v42\_1\_2 - [https://assets.sh.tines.com/releases/42.1.2-rd.json](https://assets.sh.tines.com/releases/42.1.2-rd.json)

### **July 16th, 2026 - 42.1.1**

Release Details (JSON) 3db38ddb\_v42\_1\_1 - [https://assets.sh.tines.com/releases/42.1.1-rd.json](https://assets.sh.tines.com/releases/42.1.1-rd.json)

### **July 14th, 2026 - 42.0.4**

Release Details (JSON) b54a639a\_v42\_0\_4 - [https://assets.sh.tines.com/releases/42.0.4-rd.json](https://assets.sh.tines.com/releases/42.0.4-rd.json)

### **July 11th, 2026 - 42.0.3**

Release Details (JSON) 261078c1\_v42\_0\_3 - [https://assets.sh.tines.com/releases/42.0.3-rd.json](https://assets.sh.tines.com/releases/42.0.3-rd.json)

### **July 10th, 2026 - 42.0.2**

Release Details (JSON) 195ec5af\_v42\_0\_2 - [https://assets.sh.tines.com/releases/42.0.2-rd.json](https://assets.sh.tines.com/releases/42.0.2-rd.json)

### **July 9th, 2026 - 42.0.1**

Release Details (JSON) e9140f25\_v42\_0\_1 - [https://assets.sh.tines.com/releases/42.0.1-rd.json](https://assets.sh.tines.com/releases/42.0.1-rd.json)

### **July 7th, 2026 - 41.6.4**

Release Details (JSON) ebd7fbff\_v41\_6\_4 - [https://assets.sh.tines.com/releases/41.6.4-rd.json](https://assets.sh.tines.com/releases/41.6.4-rd.json)

### **July 4th, 2026 - 41.6.3**

Release Details (JSON) 18a1072b\_v41\_6\_3 - [https://assets.sh.tines.com/releases/41.6.3-rd.json](https://assets.sh.tines.com/releases/41.6.3-rd.json)

### **July 3rd, 2026 - 41.6.2**

Release Details (JSON) 195e5e59\_v41\_6\_2 - [https://assets.sh.tines.com/releases/41.6.2-rd.json](https://assets.sh.tines.com/releases/41.6.2-rd.json)

### **July 2nd, 2026 - 41.6.1**

Release Details (JSON) e7d2f008\_v41\_6\_1 - [https://assets.sh.tines.com/releases/41.6.1-rd.json](https://assets.sh.tines.com/releases/41.6.1-rd.json)

### **June 30th, 2026 - 41.5.4**

Release Details (JSON) 9fac3e24\_v41\_5\_4 - [https://assets.sh.tines.com/releases/41.5.4-rd.json](https://assets.sh.tines.com/releases/41.5.4-rd.json)

### **June 27th, 2026 - 41.5.3**

Release Details (JSON) fc7cba16\_v41\_5\_3 - [https://assets.sh.tines.com/releases/41.5.3-rd.json](https://assets.sh.tines.com/releases/41.5.3-rd.json)

### **June 26th, 2026 - 41.5.2**

Release Details (JSON) fd292846\_v41\_5\_2 - [https://assets.sh.tines.com/releases/41.5.2-rd.json](https://assets.sh.tines.com/releases/41.5.2-rd.json)

### **June 25th, 2026 - 41.5.1**

Release Details (JSON) c173711f\_v41\_5\_1 - [https://assets.sh.tines.com/releases/41.5.1-rd.json](https://assets.sh.tines.com/releases/41.5.1-rd.json)

### ~June 23rd, 2026 - 41.4.4~ (Deprecated, use 41.5.1)

Release Details (JSON) d2a19cdf\_v41\_4\_4 - [https://assets.sh.tines.com/releases/41.4.4-rd.json](https://assets.sh.tines.com/releases/41.4.4-rd.json)

### ~June 20th, 2026 - 41.4.3~ (Deprecated, use 41.5.1)

Release Details (JSON) 090bec25\_v41\_4\_3 - [https://assets.sh.tines.com/releases/41.4.3-rd.json](https://assets.sh.tines.com/releases/41.4.3-rd.json)

### ~June 19th, 2026 - 41.4.2~ (Deprecated, use 41.5.1)

Release Details (JSON) eaf95e31\_v41\_4\_2 - [https://assets.sh.tines.com/releases/41.4.2-rd.json](https://assets.sh.tines.com/releases/41.4.2-rd.json)

### ~June 18th, 2026 - 41.4.1~ (Deprecated, use 41.5.1)

Release Details (JSON) d90bf4f0\_v41\_4\_1 - [https://assets.sh.tines.com/releases/41.4.1-rd.json](https://assets.sh.tines.com/releases/41.4.1-rd.json)

### ~June 13th, 2026 - 41.3.4~ (Deprecated, use 41.5.1)

Release Details (JSON) 1baa8a02\_v41\_3\_4 - [https://assets.sh.tines.com/releases/41.3.4-rd.json](https://assets.sh.tines.com/releases/41.3.4-rd.json)

### ~June 12th, 2026 - 41.3.2~ (Deprecated, use 41.5.1)

Release Details (JSON) 465ed3f3\_v41\_3\_2 - [https://assets.sh.tines.com/releases/41.3.2-rd.json](https://assets.sh.tines.com/releases/41.3.2-rd.json)

### ~June 11th, 2026 - 41.3.1~ (Deprecated, use 41.5.1)

Release Details (JSON) 5ad433aa\_v41\_3\_1 - [https://assets.sh.tines.com/releases/41.3.1-rd.json](https://assets.sh.tines.com/releases/41.3.1-rd.json)

### ~June 9th, 2026 - 41.2.5~ (Deprecated, use 41.5.1)

Release Details (JSON) f2d8911c\_v41\_2\_5 - [https://assets.sh.tines.com/releases/41.2.5-rd.json](https://assets.sh.tines.com/releases/41.2.5-rd.json)

### ~June 8th, 2026 - 41.2.4~ (Deprecated, use 41.5.1)

Release Details (JSON) e00fb771\_v41\_2\_4 - [https://assets.sh.tines.com/releases/41.2.4-rd.json](https://assets.sh.tines.com/releases/41.2.4-rd.json)

-   Fixed an issue on self-hosted instances where HTTP Request actions were incorrectly blocked from connecting to private IP addresses.

### ~June 5th, 2026 - 41.2.3~ (Deprecated, use 41.5.1)

Release Details (JSON) b9968081\_v41\_2\_3 - [https://assets.sh.tines.com/releases/41.2.3-rd.json](https://assets.sh.tines.com/releases/41.2.3-rd.json)

### ~June 5th, 2026 - 41.2.2~ (Deprecated, use 41.5.1)

Release Details (JSON) 4efac4c0\_v41\_2\_2 - [https://assets.sh.tines.com/releases/41.2.2-rd.json](https://assets.sh.tines.com/releases/41.2.2-rd.json)

### **June 4th, 2026 - 41.2.1**

Release Details (JSON) 25a95de3\_v41\_2\_1 - [https://assets.sh.tines.com/releases/41.2.1-rd.json](https://assets.sh.tines.com/releases/41.2.1-rd.json)

### **June 2nd, 2026 - 41.1.4**

Release Details (JSON) ea49a14b\_v41\_1\_4 - [https://assets.sh.tines.com/releases/41.1.4-rd.json](https://assets.sh.tines.com/releases/41.1.4-rd.json)

### **May 29th, 2026 - 41.1.3**

Release Details (JSON) 31d0687f\_v41\_1\_3 - [https://assets.sh.tines.com/releases/41.1.3-rd.json](https://assets.sh.tines.com/releases/41.1.3-rd.json)

### **May 28th, 2026 - 41.1.2**

Release Details (JSON) 22c0ad1b\_v41\_1\_2 - [https://assets.sh.tines.com/releases/41.1.2-rd.json](https://assets.sh.tines.com/releases/41.1.2-rd.json)

### **May 27th, 2026 - 41.1.1**

Release Details (JSON) e6c3377a\_v41\_1\_1 - [https://assets.sh.tines.com/releases/41.1.1-rd.json](https://assets.sh.tines.com/releases/41.1.1-rd.json)

### **May 22nd, 2026 - 41.0.2**

Release Details (JSON) 28dc5176\_v41\_0\_2 - [https://assets.sh.tines.com/releases/41.0.2-rd.json](https://assets.sh.tines.com/releases/41.0.2-rd.json)

### **May 21st, 2026 - 41.0.1**

Release Details (JSON) fb1a8f5b\_v41\_0\_1 - [https://assets.sh.tines.com/releases/41.0.1-rd.json](https://assets.sh.tines.com/releases/41.0.1-rd.json)

## MCP server

The Tines MCP server is a first-party [Model Context Protocol](https://modelcontextprotocol.io/) endpoint built into your Tines tenant. Connect it to an AI client such as ChatGPT, Codex, Cursor, Claude Code, or Claude Desktop, describe what you want in plain language, and your client uses the same story authoring capabilities as [Workbench for Stories](https://www.tines.com/docs/workbench-for-storyboard/) in build mode to create and modify stories on your behalf.

## **When to use this**

Use the Tines MCP server when you want to build or change stories from the tool where you already work. For example:

- "Add an HTTP action that creates a Jira issue when this webhook fires."
- "Read the current story and add error handling on the Slack action."
- "Create a new story that polls CrowdStrike and posts to Teams."

Your client turns that into story authoring operations against your tenant. You stay in the conversation; Tines updates on the storyboard.

This is different from a custom MCP server you build inside Tines.

## **Tines MCP server vs. custom MCP servers in Tines**

Tines offers two different MCP surfaces. They solve opposite problems.

<table border="1" style="border-collapse: collapse;"><colgroup><col style="width: 19.9957%;"><col style="width: 33.8541%;"><col style="width: 46.1286%;"></colgroup><tbody><tr><th></th><th><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlRpbmVzJTIwTUNQJTIwc2VydmVyJTIwKHRoaXMlMjBwYWdlKSUyMiUyQyUyMnN0cm9uZyUyMiUzQXRydWUlN0QlNUQlN0QlNUQ="><strong>Tines MCP server (this page)</strong></div></th><th><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkN1c3RvbSUyME1DUCUyMHNlcnZlciUyMGluJTIwVGluZXMlMjIlMkMlMjJzdHJvbmclMjIlM0F0cnVlJTdEJTVEJTdEJTVE"><strong>Custom MCP server in Tines</strong></div></th></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlB1cnBvc2UlMjIlMkMlMjJzdHJvbmclMjIlM0F0cnVlJTdEJTVEJTdEJTVE"><strong>Purpose</strong></div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkxldHMlMjBleHRlcm5hbCUyMEFJJTIwY2xpZW50cyUyMHVzZSUyMFRpbmVzJTIwdG8lMjBhdXRob3IlMjBzdG9yaWVzJTIyJTdEJTVEJTdEJTVE">Lets external AI clients use Tines to author stories</div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkxldHMlMjBleHRlcm5hbCUyMEFJJTIwY2xpZW50cyUyMHVzZSUyMHlvdXIlMjBUaW5lcyUyMHdvcmtmbG93cyUyMGFuZCUyMGRhdGElMjIlN0QlNUQlN0QlNUQ=">Lets external AI clients use your Tines workflows and data</div></td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMldobyUyMGNvbmZpZ3VyZXMlMjBpdCUyMiUyQyUyMnN0cm9uZyUyMiUzQXRydWUlN0QlNUQlN0QlNUQ="><strong>Who configures it</strong></div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkJ1aWx0JTIwaW50byUyMHlvdXIlMjB0ZW5hbnQlM0IlMjBjb25uZWN0JTIwd2l0aCUyME9BdXRoJTIyJTdEJTVEJTdEJTVE">Built into your tenant; connect with OAuth</div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMllvdSUyMGJ1aWxkJTIwaXQlMjBhcyUyMGFuJTIwTUNQJTIwc2VydmVyJTIwYWN0aW9uJTIwaW4lMjBhJTIwc3RvcnklMjIlN0QlNUQlN0QlNUQ=">You build it as an MCP server action in a story</div></td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMkVuZHBvaW50JTIyJTJDJTIyc3Ryb25nJTIyJTNBdHJ1ZSU3RCU1RCU3RCU1RA=="><strong>Endpoint</strong></div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGeW91ci10ZW5hbnQudGluZXMuY29tJTJGbWNwJTIyJTdEJTVEJTdEJTVE">https://your-tenant.tines.com/mcp</div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMmh0dHBzJTNBJTJGJTJGeW91ci10ZW5hbnQudGluZXMuY29tJTJGbWNwJTJGJTIyJTdEJTVEJTdEJTVE">https://your-tenant.tines.com/mcp/&lt;string&gt;</div></td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMldoYXQlMjBjbGllbnRzJTIwY2FuJTIwZG8lMjIlMkMlMjJzdHJvbmclMjIlM0F0cnVlJTdEJTVEJTdEJTVE"><strong>What clients can do</strong></div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlN0b3J5JTIwY29waWxvdCUyMGJ1aWxkJTIwY2FwYWJpbGl0aWVzJTIwKHJlYWQlMkMlMjBjcmVhdGUlMkMlMjB1cGRhdGUlMjBzdG9yaWVzJTJDJTIwYW5kJTIwc28lMjBvbiklMjIlN0QlNUQlN0QlNUQ=">Workbench for Storyboard build capabilities (read, create, update stories, and so on)</div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMldoYXRldmVyJTIweW91JTIwZXhwb3NlJTIwYnklMjBsaW5raW5nJTIwYWN0aW9ucyUyMGFzJTIwdG9vbHMlMjIlN0QlNUQlN0QlNUQ=">Whatever you expose by linking actions as tools</div></td></tr><tr><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMlR5cGljYWwlMjB1c2UlMjIlMkMlMjJzdHJvbmclMjIlM0F0cnVlJTdEJTVEJTdEJTVE"><strong>Typical use</strong></div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMiU1QyUyMkluJTIwQ3Vyc29yJTJDJTIwaGVscCUyMG1lJTIwYnVpbGQlMjB0aGlzJTIwVGluZXMlMjBzdG9yeSU1QyUyMiUyMiU3RCU1RCU3RCU1RA==">"In Cursor, help me build this Tines story"</div></td><td><div data-slate-fragment="JTVCJTdCJTIydHlwZSUyMiUzQSUyMnBhcmFncmFwaCUyMiUyQyUyMmNoaWxkcmVuJTIyJTNBJTVCJTdCJTIydGV4dCUyMiUzQSUyMiU1QyUyMkluJTIwQ2xhdWRlJTJDJTIwY2FsbCUyMG15JTIwVGluZXMlMjBhdXRvbWF0aW9uJTIwdG8lMjBsb29rJTIwdXAlMjB0aWNrZXRzJTVDJTIyJTIyJTdEJTVEJTdEJTVE">"In Claude, call my Tines automation to look up tickets"</div></td></tr></tbody></table>

Use the **Tines MCP server** when the AI should work inside Tines' editing stories on your behalf from Cursor, Claude Code, or similar.

Use a **custom MCP server in Tines** when the AI should call your stories' lookups, approvals, internal APIs, or other logic you have already modeled as story actions. See [Build an MCP server in Tines](file:///docs/actions/templates/mcp-server/) for how to create one, link tools, and connect clients.

You can use both: an external client might use the Tines MCP server to author stories and a separate custom MCP server to run your production integrations.

## **How it works**

1. You add `https://your-tenant.tines.com/mcp` to your MCP client.
2. On first use, the client runs OAuth and you approve access on a consent screen titled **Tines MCP server**, which lists the story authoring capabilities you are granting.
3. You tell the client what you want, usually in the context of a specific story.
4. The client loads authoring guidance for that story, then automatically reads, creates, updates, and validates story contents.
5. You review the result in Tines. Each tool use is recorded in [audit logs](https://www.tines.com/docs/audit-logs/) as MCP activity.

Authentication uses OAuth only, not API keys.

Opening `https://<your-tenant>.tines.com/mcp` in a browser shows a setup page with copy-ready configuration snippets for common clients.

## Working with a story

The server exposes Workbench story building capabilities: reading and changing stories, creating and updating actions, validation, running actions where permitted, research and listing helpers, and private template operations when your permissions allow.

## Related page

- [Workbench for stories](https://www.tines.com/docs/workbench-for-storyboard/)— the in-app AI capability this server exposes
- [Build an MCP server in Tines](https://www.tines.com/docs/mcp-server/) — expose your own automations to AI tools
- [Connect AI agent actions to remote MCP servers](https://www.tines.com/docs/ai-agent/) — use external MCP tools inside Tines stories

# 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.

## Restricting API key creation

A tenant owner can restrict the ability to create API keys on a tenant with the toggle shown below:

![API Key Settings](https://www.datocms-assets.com/55802/1784115499-api_key_restriction_setting.png)

When this setting is enabled, only tenant owners and users who have been granted the tenant permission `API_KEY_CREATE` can create API keys.

## 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             |
| records_query | 200             |
| 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)                                                                                                                                      |
| include_references                         | **Optional** Set to `true` to include IDs of stories and actions referencing this credential. Pass as a query parameter.                                                                                                                  |

### 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
    }'
```

With story references:

```bash
curl -X POST \
  'https://<tenant-domain>/api/v1/user_credentials?include_references=true' \
  -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)                                                                                                                                      |
| references                                 | List of stories referencing this credential, grouped by story (each with `story_id`, `team_id`, and `action_ids`). Only included when `include_references=true`. Limited to 500 visible actions.                             |

### Sample response

<!-- cspell:disable -->

```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"
  }
}
```

<!-- cspell:enable -->

### Sample response with story references

<!-- cspell:disable -->

```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"
  },
  "references": []
}
```

<!-- cspell:enable -->

### Get

## Description

Retrieve a credential.

## Request

HTTP Method: **GET**

| Parameter          | Description                                                                                   |
| ------------------ | --------------------------------------------------------------------------------------------- |
| credential_id      | The ID of the credential to retrieve.                                                         |
| include_references | **Optional** Set to `true` to include IDs of stories and actions referencing this credential. |

```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>>'
```

With story references:

```bash
curl -X GET \
  'https://<tenant-domain>/api/v1/user_credentials/<<credential_id>>?include_references=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 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)                                                                                                                                      |
| references                                 | List of stories referencing this credential, grouped by story (each with `story_id`, `team_id`, and `action_ids`). Only included when `include_references=true`. Limited to 500 visible actions.                             |

### 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 -->

### Sample response with story references

<!-- 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"
  },
  "references": [
    {
      "story_id": 10,
      "team_id": 2,
      "action_ids": [101, 102]
    },
    {
      "story_id": 11,
      "team_id": 3,
      "action_ids": [201]
    }
  ]
}
```

<!-- 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)                                                                                                                                                 |
| include_references                         | **Optional** Set to `true` to include IDs of stories and actions referencing this credential. Pass as a query parameter.                                                                                                                             |

#### 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" }
    }'
```

With story references:

```bash
curl -X PUT \
  'https://<tenant-domain>/api/v1/user_credentials/<<credential_id>>?include_references=true' \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "Updated credential name",
        "value": "Updated secret value",
        "mode": "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 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)                                                                                                                                      |
| references                                 | List of stories referencing this credential, grouped by story (each with `story_id`, `team_id`, and `action_ids`). Only included when `include_references=true`. Limited to 500 visible actions.                             |

### Sample response

<!-- cspell:disable -->

```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"
  }
}
```

<!-- cspell:enable -->

### Sample response with story references

<!-- cspell:disable -->

```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"
  },
  "references": [
    {
      "story_id": 10,
      "team_id": 2,
      "action_ids": [101, 102]
    },
    {
      "story_id": 11,
      "team_id": 3,
      "action_ids": [201]
    }
  ]
}
```

<!-- cspell:enable -->

### List

## Description

Retrieve a list of credentials.

## Request

HTTP Method: **GET**

| Parameter          | Description                                                                                         |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| team_id            | **Optional** List credentials for the given team.                                                   |
| folder_id          | **Optional** List credentials 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`.                   |
| include_references | **Optional** Set to `true` to include IDs of stories and actions referencing this credential.       |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/user_credentials \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

With story references:

```bash
curl -X GET \
  'https://<tenant-domain>/api/v1/user_credentials?include_references=true' \
  -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)                                                                                                                                      |
| references                                 | List of stories referencing this credential, grouped by story (each with `story_id`, `team_id`, and `action_ids`). Only included when `include_references=true`. Limited to 100 visible actions per credential.              |

### 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 -->

### Sample response with story references

<!-- 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"
      },
      "references": [
        {
          "story_id": 10,
          "team_id": 2,
          "action_ids": [101, 102]
        }
      ]
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/user_credentials?include_references=true&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`                  |
| draft_name      | **Optional** Custom name for the draft when Change Control is enabled. Defaults to "Imported story" if not provided. |

```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 /> `Agents::RunScriptAgent`<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::CaseAgent`<br />`Agents::EmailAgent`<br />`Agents::EventTransformationAgent`<br />`Agents::FormAgent`<br />`Agents::GroupAgent`<br />`Agents::HTTPRequestAgent`<br />`Agents::IMAPAgent`<br />`Agents::LLMAgent`<br />`Agents::RecordAgent`<br />`Agents::RunScriptAgent`<br />`Agents::SendToStoryAgent`<br />`Agents::TriggerAgent`<br />`Agents::WebhookAgent`<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`.                                                                                      |
| exclude_previous_event_payloads | **Optional** When `true`, each event's `payload` contains only its own output, excluding payloads received from upstream events in the story run. 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 severity level (warning = 2, info = 3, error = 4). Pass a single value (`?level=4`) or multiple values (`?level[]=4&level[]=2`). |
| 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        | 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. |

> **Note:** If you're not the author of the change request, this rejects it instead of canceling it.

### 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                                                                                                                                                                                                                 |
| bypass_approval        | **Optional** Boolean. Defaults to `false`. When `true`, a pending (not yet approved) change request is promoted straight to the live story, skipping the approval step. Requires permission to promote on the story's team (`STORY_MANAGE`). |
| bypass_approval_reason | **Optional** String. A human-readable reason recorded in the audit trail. When `bypass_approval` is `true` and no reason is supplied, the recorded message defaults to "Approval bypassed via API".                                          |

| Path Parameter | Description          |
| -------------- | -------------------- |
| story_id       | The ID of the story. |

> **Note:** Bypassing approval is the supported way for a single API identity to push a change live when your tenant has "Require approval for all changes" enabled, since a user cannot approve their own change request under that setting. Bypassing uses the same permission gate as a normal promotion, so the calling user needs permission to promote on the story's team (`STORY_MANAGE`); there is no separate bypass permission. A bypass is recorded distinctly in the audit trail and triggers a notification, so it is logged separately from a normal promotion.

### 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]
}
```

#### Reject

## Description

Reject 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. |

> **Note:** If you're the author of the change request, this cancels it instead of rejecting it.

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/stories/<<story_id>>/change_request/reject  \
  -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 rejected

### 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 rejected. |

### Sample response

<!-- cspell:disable -->

```json
{
  "draft_id": 1234,
  "draft_name": "Initial draft",
  "change_request": null,
  "id": 154
}
```

<!-- cspell:enable -->

#### View

## Description

View the current change request for a story.

## 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/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**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| event_id       | The ID of the event to retrieve. |

| Query Parameter                 | Description                                                                                                                                                                           |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| exclude_previous_event_payloads | **Optional** When `true`, the `payload` field contains only this event's own output, excluding payloads received from upstream events in the story run. Boolean, defaults to `false`. |

```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. (Avoid using with story_id. When team_id and story_id are used together, story_id is ignored.)                                           |
| story_id                        | **Optional** Filter by the given story. (Avoid using with team_id. When team_id and story_id are used together, story_id is ignored.)                                           |
| 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`.                                                                                      |
| exclude_previous_event_payloads | **Optional** When `true`, each event's `payload` contains only its own output, excluding payloads received from upstream events in the story run. 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

#### Get run summary

## Description

Retrieve metadata for a specific 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.                                                                                                 |
| draft_id        | **Optional** Return the run 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>>/summary \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the story run.

### 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_run": {
    "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"
  }
}
```

<!-- cspell:enable -->

#### 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                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| scope                  | **Optional** Filter teams by scope. One of `all`, `standard`, or `personal_only`. Default: `standard`.    |
| include_personal_teams | **Optional** _Deprecated: use `scope` instead._ 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                                                                                                                                                                                                          |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| team_id            | The team ID the case is within.                                                                                                                                                                                                                                                                                      |
| name               | The case name.                                                                                                                                                                                                                                                                                                       |
| template_id        | **Optional** Create the case from a template. Pass a numeric template ID (from the [List case templates API](/api/cases/case_templates/list)) or a Made by Tines template slug (string). Template defaults are applied for any fields not explicitly provided in the request.                                        |
| 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 field IDs and values for the case. Field IDs provided must exist within the team for the field to be successfully added to the case. For fields with "Allow multiple" enabled, provide the value as a pipe-delimited string, for example `"option1\|option2"`. |
| 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="
        }
      ]
    }
  ]
}'
```

### Sample request with template

Create a case from a template by passing `template_id`. Any fields you provide override the template defaults.

```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": "Phishing investigation",
  "template_id": 12
}'
```

You can also pass a Made by Tines template slug as a string:

```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": "Phishing investigation",
  "template_id": "phishing-investigation"
}'
```

## 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.                                             |
| linked_cases_meta  | Metadata defining pagination params required to fetch additional linked cases 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": 2, "name": "Case 2" },
    { "case_id": 3, "name": "Case 3" }
  ],
  "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
  },
  "linked_cases_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/linked_cases?per_page=50&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 50,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Get

| ⚠️ Note                                                                                                                                                         |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| This v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/get) instead. |

## 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

A case is a collaborative record where a team can organize information, comment, take action, and track progress — for incidents, alerts, support requests, or any operational workflow. See [Cases](https://www.tines.com/docs/cases/) in the Tines docs to learn more.

Retrieve a single case.

## Request

HTTP Method: **GET**

| Parameter        | Description                                                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| case_id          | The ID of the case to retrieve.                                                                                                 |
| resolve_mentions | **Optional** When set to `true`, replaces mention tokens in the response's text fields with display names. Defaults to `false`. |

```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.                                                                                    |
| linked_cases_meta  | Metadata defining pagination params required to fetch additional linked cases for the case.                                           |
| 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": 2, "name": "Case 2" },
    { "case_id": 3, "name": "Case 3" }
  ],
  "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
  },
  "linked_cases_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/linked_cases?per_page=50&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 50,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Update

| ⚠️ Note                                                                                                                                                            |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| This v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/update) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.                                             |
| linked_cases_meta  | Metadata defining pagination params required to fetch additional linked cases 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": 2, "name": "Case 2" },
    { "case_id": 3, "name": "Case 3" }
  ],
  "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
  },
  "linked_cases_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/linked_cases?per_page=50&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 50,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### List

| ⚠️ Note                                                                                                                                                          |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| This v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about 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 field values. Each field object contains:<br/>• `key`: The slug of the 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 field values using AND logic. Each field object contains:<br/>• `key`: The slug of the 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 field values using OR logic. Each field object contains:<br/>• `key`: The slug of the 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.                                             |
| linked_cases_meta  | Metadata defining pagination params required to fetch additional linked cases 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": 2, "name": "Case 2" },
        { "case_id": 3, "name": "Case 3" }
      ],
      "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
      },
      "linked_cases_meta": {
        "current_page": "https://<tenant-domain>/api/v2/cases/1/linked_cases?per_page=50&page=1",
        "previous_page": null,
        "next_page": null,
        "next_page_number": null,
        "per_page": 50,
        "pages": 1,
        "count": 2
      }
    }
  ],
  "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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.                                                                                    |
| linked_cases_meta  | Metadata defining pagination params required to fetch additional linked cases for the case.                                           |
| 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": 2, "name": "Case 2" },
    { "case_id": 3, "name": "Case 3" }
  ],
  "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
  },
  "linked_cases_meta": {
    "current_page": "https://<tenant-domain>/api/v2/cases/1/linked_cases?per_page=50&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 50,
    "pages": 1,
    "count": 2
  }
}
```

<!-- cspell:enable -->

### Actions

#### Create

| ⚠️ Note                                                                                                                                                                    |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| This v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/actions/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/actions/get) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/actions/update) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/actions/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/actions/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Query Parameter  | Description                                                                                                                                |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| case_id          | The ID of the case.                                                                                                                        |
| activity_id      | The ID of the case activity.                                                                                                               |
| resolve_mentions | **Optional** When set to `true` and the activity is a comment, replaces mention tokens in `value` with display names. Defaults to `false`. |

```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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.                                       |
| resolve_mentions | **Optional** When set to `true`, replaces mention tokens in comment-type activity `value` fields with display names. Defaults to `false`. |

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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/assignees/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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

A case block is a reorderable section of case content below the description, with its own title, type, color, and visibility. See [Case blocks](https://www.tines.com/docs/cases/overview/content-updates/case-blocks) in the Tines docs to learn more.

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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Block API](/api/cases/blocks/get) to learn more about case blocks.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 field to use with the field value.                                                                                              |
| 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. |

> **Note**: For fields with "Allow multiple" enabled, provide `value` as a pipe-delimited string, for example `"option1|option2"`.

### 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"
          }
        }
      ]
    }
  ]
}
```

### Fields

#### Create

## Description

Creates a field on a team.

See the [Get Field API](/api/cases/case_inputs/get) to learn more about fields.

## Request

HTTP Method: **POST**

| Parameter          | Description                                                                                                                            |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| name               | The name of the field.                                                                                                                 |
| input_type         | The type of the field. One of `string`, `number`, `boolean`, or `timestamp`.                                                           |
| team_id            | The ID of the team where the field 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 field. One of `options` or `regex`. Use these only with `string` inputs.                      |
| validation_options | **Optional** Validation options for the field. 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 field 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 field.

### Field description

| Parameter             | Description                                                                                                              |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| id                    | The field ID.                                                                                                            |
| name                  | The name of the field.                                                                                                   |
| input_type            | The type of the field.                                                                                                   |
| 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 field.                                                                                            |
| validation_options    | Validation options of the field.                                                                                         |
| team                  | An object containing the ID and name of the team the field belongs to.                                                   |
| team_case_input_group | An object containing the name of the group the field belongs to, if any.                                                 |
| created_at            | An ISO 8601 timestamp representing when the field was created.                                                           |
| updated_at            | An ISO 8601 timestamp representing when the field 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

A field is the team-level definition of a case field — its type, validation, and any group it belongs to. Fields are referred to as `case_inputs` in API endpoint paths and response keys. See [Case fields](https://www.tines.com/docs/cases/case-fields) in the Tines docs to learn more.

Returns a field.

## Request

HTTP Method: **GET**

| Query Parameter | Description                      |
| --------------- | -------------------------------- |
| case_input_id   | The ID of the field 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 field.

### Field description

| Parameter             | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| id                    | The field ID.                                                                                                             |
| name                  | The name of the field.                                                                                                    |
| key                   | The lookup key of the field.                                                                                              |
| input_type            | The type of the field.                                                                                                    |
| 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 field. 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 field belongs to.                                                    |
| team_case_input_group | An object containing the name of the group the field belongs to, if any.                                                  |
| created_at            | An ISO 8601 timestamp representing when the field was created.                                                            |
| updated_at            | An ISO 8601 timestamp representing when the field 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 fields.

See the [Get Field API](/api/cases/case_inputs/get) to learn more about fields.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Only include fields 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 fields.

### Field description

| Parameter             | Description                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| id                    | The field ID.                                                                                                             |
| name                  | The name of the field.                                                                                                    |
| key                   | The lookup key of the field.                                                                                              |
| input_type            | The type of the field.                                                                                                    |
| 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 field. 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 field belongs to.                                                    |
| team_case_input_group | An object containing the name of the group the field belongs to, if any.                                                  |
| created_at            | An ISO 8601 timestamp representing when the field was created.                                                            |
| updated_at            | An ISO 8601 timestamp representing when the field 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
  }
}
```

#### Field values

##### List

## Description

Returns a list of field values for a field.

See the [Get Field API](/api/cases/case_inputs/get) to learn more about fields.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| case_input_id   | The ID of the field that the field values 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 field values for the field.

### 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 field. |

### 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 -->

### Case statuses

#### Create

## Description

Create a case status for a team.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **POST**

| Body Parameter | Description                                                                                   |
| -------------- | --------------------------------------------------------------------------------------------- |
| team_id        | ID of the team the status belongs to.                                                         |
| name           | Case status name.                                                                             |
| parent_status  | **Optional** The parent status the status sits under: `OPEN` or `CLOSED`. Defaults to `OPEN`. |
| description    | **Optional** A description for the status.                                                    |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/case_sub_statuses \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "team_id": 1,
        "name": "Triage",
        "parent_status": "OPEN",
        "description": "Newly opened cases awaiting review."
      }'
```

## Response

A successful request will return a JSON object describing the created case status.

### Field description

| Parameter     | Description                                                               |
| ------------- | ------------------------------------------------------------------------- |
| id            | Case status ID.                                                           |
| name          | Case status name.                                                         |
| slug          | A human-readable identifier for the status, auto-generated from the name. |
| description   | Case status description, or `null` if none is set.                        |
| parent_status | The parent status the status belongs to: `OPEN` or `CLOSED`.              |
| default       | Whether this is the default status for its parent status.                 |
| team          | An object containing the `id` of the team the status belongs to.          |
| created_at    | ISO 8601 timestamp describing when the status was created.                |
| updated_at    | ISO 8601 timestamp describing when the status was last updated.           |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 41,
  "name": "Triage",
  "slug": "triage",
  "description": "Newly opened cases awaiting review.",
  "parent_status": "OPEN",
  "default": false,
  "team": {
    "id": 1
  },
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T09:15:42.128Z"
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single case status.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Path Parameter     | Description                            |
| ------------------ | -------------------------------------- |
| case_sub_status_id | The ID of the case status to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_sub_statuses/<<case_sub_status_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 case status.

### Field description

| Parameter     | Description                                                               |
| ------------- | ------------------------------------------------------------------------- |
| id            | Case status ID.                                                           |
| name          | Case status name.                                                         |
| slug          | A human-readable identifier for the status, auto-generated from the name. |
| description   | Case status description, or `null` if none is set.                        |
| parent_status | The parent status the status belongs to: `OPEN` or `CLOSED`.              |
| default       | Whether this is the default status for its parent status.                 |
| team          | An object containing the `id` of the team the status belongs to.          |
| created_at    | ISO 8601 timestamp describing when the status was created.                |
| updated_at    | ISO 8601 timestamp describing when the status was last updated.           |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 41,
  "name": "Triage",
  "slug": "triage",
  "description": "Newly opened cases awaiting review.",
  "parent_status": "OPEN",
  "default": true,
  "team": {
    "id": 1
  },
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T09:15:42.128Z"
}
```

<!-- cspell:enable -->

#### Update

## Description

Update a case status.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **PUT**

| Path Parameter     | Description                          |
| ------------------ | ------------------------------------ |
| case_sub_status_id | The ID of the case status to update. |

| Body Parameter | Description                            |
| -------------- | -------------------------------------- |
| name           | **Optional** Updated case status name. |
| description    | **Optional** Updated description.      |

### Sample request

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/case_sub_statuses/<<case_sub_status_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "name": "In triage",
        "description": "Cases currently being reviewed."
      }'
```

## Response

A successful request will return a JSON object describing the updated case status.

### Field description

| Parameter     | Description                                                                                                       |
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
| id            | Case status ID.                                                                                                   |
| name          | Case status name.                                                                                                 |
| slug          | A human-readable identifier for the status, auto-generated from the name. Updating the name regenerates the slug. |
| description   | Case status description, or `null` if none is set.                                                                |
| parent_status | The parent status the status belongs to: `OPEN` or `CLOSED`.                                                      |
| default       | Whether this is the default status for its parent status.                                                         |
| team          | An object containing the `id` of the team the status belongs to.                                                  |
| created_at    | ISO 8601 timestamp describing when the status was created.                                                        |
| updated_at    | ISO 8601 timestamp describing when the status was last updated.                                                   |

### Sample response

<!-- cspell:disable -->

```json
{
  "id": 41,
  "name": "In triage",
  "slug": "in_triage",
  "description": "Cases currently being reviewed.",
  "parent_status": "OPEN",
  "default": false,
  "team": {
    "id": 1
  },
  "created_at": "2026-04-10T09:15:42.128Z",
  "updated_at": "2026-04-10T11:04:03.772Z"
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of case statuses for a team. Each status belongs to one of the two parent statuses, `OPEN` or `CLOSED`.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                         |
| --------------- | ----------------------------------------------------------------------------------- |
| team_id         | The ID of the team whose case statuses you want to retrieve.                        |
| parent_status   | **Optional** Return only statuses under this parent status: `OPEN` or `CLOSED`.     |
| search          | **Optional** Return only statuses whose name contains this text (case insensitive). |
| 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/case_sub_statuses?team_id=1" \
  -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 case statuses the requesting token has access to.

### Field description

| Parameter     | Description                                                               |
| ------------- | ------------------------------------------------------------------------- |
| id            | Case status ID.                                                           |
| name          | Case status name.                                                         |
| slug          | A human-readable identifier for the status, auto-generated from the name. |
| description   | Case status description, or `null` if none is set.                        |
| parent_status | The parent status the status belongs to: `OPEN` or `CLOSED`.              |
| default       | Whether this is the default status for its parent status.                 |
| team          | An object containing the `id` of the team the status belongs to.          |
| created_at    | ISO 8601 timestamp describing when the status was created.                |
| updated_at    | ISO 8601 timestamp describing when the status was last updated.           |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_sub_statuses": [
    {
      "id": 41,
      "name": "Triage",
      "slug": "triage",
      "description": "Newly opened cases awaiting review.",
      "parent_status": "OPEN",
      "default": true,
      "team": {
        "id": 1
      },
      "created_at": "2026-04-10T09:15:42.128Z",
      "updated_at": "2026-04-10T09:15:42.128Z"
    },
    {
      "id": 42,
      "name": "Resolved",
      "slug": "resolved",
      "description": null,
      "parent_status": "CLOSED",
      "default": true,
      "team": {
        "id": 1
      },
      "created_at": "2026-04-10T09:21:11.491Z",
      "updated_at": "2026-04-10T09:21:11.491Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/case_sub_statuses?team_id=1&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 case status. Any cases currently using the deleted status are reassigned to the fallback status you provide.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **DELETE**

| Path Parameter     | Description                          |
| ------------------ | ------------------------------------ |
| case_sub_status_id | The ID of the case status to delete. |

| Query Parameter    | Description                                                                      |
| ------------------ | -------------------------------------------------------------------------------- |
| fallback_status_id | The ID of the case status that cases using the deleted status are reassigned to. |

### Sample request

```bash
curl -X DELETE \
  "https://<tenant-domain>/api/v1/case_sub_statuses/<<case_sub_status_id>>?fallback_status_id=42" \
  -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.

### Case templates

#### List

## Description

Retrieve a list of case templates for a team.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Required** Return case templates belonging to this team.                                          |
| search          | **Optional** A string that searches against case template name.                                     |
| per_page        | **Optional** Set the number of results returned per page. Defaults to 20, maximum is 100.           |
| 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_templates?team_id=<<team_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 templates the requesting token has access to.

### Field description

| Parameter  | Description                                                                      |
| ---------- | -------------------------------------------------------------------------------- |
| id         | The case template ID.                                                            |
| name       | The case template name.                                                          |
| emoji      | The emoji associated with the case template.                                     |
| team       | The team the case template belongs to - ID and name.                             |
| created_at | ISO 8601 timestamp representing the date and time the case template was created. |
| updated_at | ISO 8601 timestamp representing the date and time the case template was updated. |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_templates": [
    {
      "id": 12,
      "name": "Phishing investigation",
      "emoji": "🎣",
      "team": {
        "id": 2,
        "name": "Team 1"
      },
      "created_at": "2026-05-25T10:00:00Z",
      "updated_at": "2026-05-25T10:00:00Z"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/case_templates?team_id=2&per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

<!-- cspell:enable -->

#### Export

## Description

Export a case template to a transferable JSON file. The result can be loaded with the [import](/api/cases/case_templates/import) endpoint to recreate the template on the same tenant or on another tenant.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Path Parameter | Description                            |
| -------------- | -------------------------------------- |
| id             | The ID of the case template to export. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_templates/<<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 case template in exported form.

### Field description

| Parameter      | Description                                                     |
| -------------- | --------------------------------------------------------------- |
| schema_version | Version of the case template export schema.                     |
| exported_at    | ISO 8601 timestamp representing when the template was exported. |
| templates      | An array of exported case templates.                            |

Each entry in `templates` contains:

| Parameter         | Description                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| name              | The case template name.                                                                                       |
| emoji             | The emoji associated with the case template.                                                                  |
| options           | An object describing the case template configuration. See [Options fields](#options-fields) below.            |
| input_values      | An array of field values defined on the template, referenced by field slug.                                   |
| field_definitions | An array describing the fields referenced by the template, so the template can be imported into another team. |

### Options fields

| Parameter          | Description                                                                                                                                                                                                                                                                                                                   |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| case_name          | **Optional** The case name template. May contain field mentions written as `<@case_input-slug:my_field>` or `:case_input{#slug:my_field}`, which are rewritten to use IDs on import.                                                                                                                                          |
| case_description   | **Optional** The case description template. Supports markdown and field mentions, which are rewritten to use field slugs on export and back to IDs on import.                                                                                                                                                                 |
| priority           | **Optional** The case priority - critical, high, medium, low or info.                                                                                                                                                                                                                                                         |
| tag_names          | **Optional** An array of tag name strings. Tag IDs from the source team are resolved to names on export and recreated on the destination team on import.                                                                                                                                                                      |
| sub_status         | **Optional** An object describing the case sub-status, with keys `name`, `parent_status` (`open` or `closed`), and `icon`. Resolved from the source team's sub-status ID on export and matched or created on import.                                                                                                          |
| assignee_emails    | **Optional** An array of user email addresses. Assignee IDs stored on the template are resolved to emails on export so the template can round-trip across tenants. Formula entries are preserved.                                                                                                                             |
| team_case_buttons  | **Optional** An array of button objects for downstream actions associated with the case. Each object has `button_type` (one of `webhook`, `story`, `page`, or `url`), `url` (tenant-specific webhook URLs may need updating after import to another tenant), `label` (tooltip), and `button_text` (text shown on the button). |
| blocks             | **Optional** An array of block objects associated with the case. Each object has `block_type`, `title`, and an `elements` array whose shape varies by `block_type`. Field mentions inside block element `content` are rewritten to use field slugs on export.                                                                 |
| team_case_tasks    | **Optional** An array of task objects associated with the case. Each object has `description` and an optional `assignee_emails` array; assignee IDs stored on the task are resolved to emails on export.                                                                                                                      |
| 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. Each entry has a `name` and a `formula`.                                                                                                                                   |
| metadata           | **Optional** Case-related metadata represented as key-value pairs.                                                                                                                                                                                                                                                            |

### Sample response

<!-- cspell:disable -->

```json
{
  "schema_version": 1,
  "exported_at": "2026-05-25T10:00:00Z",
  "templates": [
    {
      "name": "Phishing investigation",
      "emoji": "🎣",
      "options": {
        "case_name": "Phishing report for <<recipient>>",
        "case_description": "Investigate reported phishing email from <@case_input-slug:reporter_email>.",
        "priority": "high",
        "tag_names": ["phishing", "email"],
        "sub_status": {
          "name": "Triage",
          "parent_status": "open",
          "icon": "in_progress"
        },
        "assignee_emails": ["analyst@example.com"],
        "team_case_buttons": [
          {
            "button_type": "page",
            "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
            "label": "Isolate host and give reason",
            "button_text": "Open form"
          }
        ],
        "blocks": [
          {
            "title": "Investigation notes",
            "block_type": "note",
            "elements": [
              {
                "content": "Initial triage required for reporter <@case_input-slug:reporter_email>.",
                "note_type": "text",
                "color": "gold"
              }
            ]
          }
        ],
        "team_case_tasks": [
          {
            "description": "Review headers and attachments",
            "assignee_emails": ["analyst@example.com"]
          }
        ],
        "closure_conditions": [
          {
            "name": "has at least 1 assignee",
            "formula": "=SIZE(team_case.assignees) > 0"
          }
        ],
        "metadata": {
          "source": "user_report"
        }
      },
      "input_values": [
        {
          "field_slug": "reporter_email",
          "value": "user@example.com"
        }
      ],
      "field_definitions": [
        {
          "slug": "reporter_email",
          "name": "Reporter email",
          "input_type": "string",
          "sensitive": false
        }
      ]
    }
  ]
}
```

<!-- cspell:enable -->

#### Import

## Description

Import a case template into a team from a previously [exported](/api/cases/case_templates/export) payload. Fields referenced by the template are resolved against the destination team, so templates can move between teams and tenants.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **POST**

| Parameter | Description                                                                                                      |
| --------- | ---------------------------------------------------------------------------------------------------------------- |
| team_id   | The ID of the team to import the case template into.                                                             |
| data      | JSON object representing the case template in [exported form](/api/cases/case_templates/export#sample-response). |

The `options` object inside each template describes the case template configuration. See [Options fields](#options-fields) below for the shape of each key.

### Options fields

| Parameter          | Description                                                                                                                                                                                                                                                                                                                   |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| case_name          | **Optional** The case name template. May contain field mentions written as `<@case_input-slug:my_field>` or `:case_input{#slug:my_field}`, which are rewritten to use IDs in the destination team on import.                                                                                                                  |
| case_description   | **Optional** The case description template. Supports markdown and field mentions, which are rewritten to use destination-team field IDs on import.                                                                                                                                                                            |
| priority           | **Optional** The case priority - critical, high, medium, low or info.                                                                                                                                                                                                                                                         |
| tag_names          | **Optional** An array of tag name strings. Tags are found or created on the destination team.                                                                                                                                                                                                                                 |
| sub_status         | **Optional** An object describing the case sub-status, with keys `name`, `parent_status` (`open` or `closed`), and `icon`. Matched against an existing sub-status on the destination team or created if not found.                                                                                                            |
| assignee_emails    | **Optional** An array of user email addresses. Emails that do not match a user on the destination team are dropped. Formula entries are preserved.                                                                                                                                                                            |
| team_case_buttons  | **Optional** An array of button objects for downstream actions associated with the case. Each object has `button_type` (one of `webhook`, `story`, `page`, or `url`), `url` (tenant-specific webhook URLs may need updating after import to another tenant), `label` (tooltip), and `button_text` (text shown on the button). |
| blocks             | **Optional** An array of block objects associated with the case. Each object has `block_type`, `title`, and an `elements` array whose shape varies by `block_type`. Field mentions inside block element `content` are rewritten to use destination-team field IDs on import.                                                  |
| team_case_tasks    | **Optional** An array of task objects associated with the case. Each object has `description` and an optional `assignee_emails` array; emails that do not match a user on the destination team are dropped on import.                                                                                                         |
| 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. Each entry has a `name` and a `formula`.                                                                                                                                   |
| metadata           | **Optional** Case-related metadata represented as key-value pairs.                                                                                                                                                                                                                                                            |

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/case_templates/import \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
        "team_id": 1,
        "data": {
          "schema_version": 1,
          "exported_at": "2026-05-25T10:00:00Z",
          "templates": [
            {
              "name": "Phishing investigation",
              "emoji": "🎣",
              "options": {
                "case_name": "Phishing report for <<recipient>>",
                "case_description": "Investigate reported phishing email from <@case_input-slug:reporter_email>.",
                "priority": "high",
                "tag_names": ["phishing", "email"],
                "sub_status": {
                  "name": "Triage",
                  "parent_status": "open",
                  "icon": "in_progress"
                },
                "assignee_emails": ["analyst@example.com"],
                "team_case_buttons": [
                  {
                    "button_type": "page",
                    "url": "https://tenant.tines.com/pages/55e94b9b4e26175051c0287f4259363f/",
                    "label": "Isolate host and give reason",
                    "button_text": "Open form"
                  }
                ],
                "blocks": [
                  {
                    "title": "Investigation notes",
                    "block_type": "note",
                    "elements": [
                      {
                        "content": "Initial triage required for reporter <@case_input-slug:reporter_email>.",
                        "note_type": "text",
                        "color": "gold"
                      }
                    ]
                  }
                ],
                "team_case_tasks": [
                  {
                    "description": "Review headers and attachments",
                    "assignee_emails": ["analyst@example.com"]
                  }
                ],
                "closure_conditions": [
                  {
                    "name": "has at least 1 assignee",
                    "formula": "=SIZE(team_case.assignees) > 0"
                  }
                ],
                "metadata": {
                  "source": "user_report"
                }
              },
              "input_values": [
                {
                  "field_slug": "reporter_email",
                  "value": "user@example.com"
                }
              ],
              "field_definitions": [
                {
                  "slug": "reporter_email",
                  "name": "Reporter email",
                  "input_type": "string",
                  "sensitive": false
                }
              ]
            }
          ]
        }
      }'
```

## Response

A successful request returns a JSON object containing an array of the case templates created in the destination team.

### Field description

| Parameter      | Description                                       |
| -------------- | ------------------------------------------------- |
| case_templates | An array of case templates created by the import. |

Each entry in `case_templates` contains:

| Parameter  | Description                                                               |
| ---------- | ------------------------------------------------------------------------- |
| id         | The case template ID.                                                     |
| name       | The case template name.                                                   |
| emoji      | The emoji associated with the case template.                              |
| team       | An object containing the ID and name of the team the template belongs to. |
| created_at | ISO 8601 timestamp representing when the case template was created.       |
| updated_at | ISO 8601 timestamp representing when the case template was last updated.  |

### Sample response

<!-- cspell:disable -->

```json
{
  "case_templates": [
    {
      "id": 4821,
      "name": "Phishing investigation",
      "emoji": "🎣",
      "team": {
        "id": 1,
        "name": "Security"
      },
      "created_at": "2026-05-25T10:05:00Z",
      "updated_at": "2026-05-25T10:05:00Z"
    }
  ]
}
```

<!-- cspell:enable -->

### Comments

#### Create

| ⚠️ Note                                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| This v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/comments/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/comments/update) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/comments/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/comments/reactions/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/comments/reactions/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 -->

### Field values

#### Create

## Description

Add a field value to a case.

See the [Get Field Value API](/api/cases/fields/get) to learn more about field values.

## Request

HTTP Method: **POST**

| Parameter | Description                                      |
| --------- | ------------------------------------------------ |
| input_id  | The ID of the field to use with the field value. |
| value     | The value of the field.                          |

| Path Parameter | Description         |
| -------------- | ------------------- |
| case_id        | The ID of the case. |

> **Note**: For fields with "Allow multiple" enabled, provide `value` as a pipe-delimited string, for example `"option1|option2"`.

### 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

A field value stores custom, structured data on a case — ticket IDs, affected systems, investigation outcomes — beyond the built-in properties. See [Case fields](https://www.tines.com/docs/cases/case-fields) in the Tines docs to learn more.

Retrieve a single field value 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 field value.

See the [Get Field Value API](/api/cases/fields/get) to learn more about field values.

## Request

HTTP Method: **PUT**

| Parameter | Description             |
| --------- | ----------------------- |
| value     | The value of the field. |

| Path Parameter | Description          |
| -------------- | -------------------- |
| case_id        | The ID of the case.  |
| field_id       | The ID of the field. |

> **Note**: For fields with "Allow multiple" enabled, provide `value` as a pipe-delimited string, for example `"option1|option2"`.

### 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 field value.

### 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 field values for a case.

See the [Get Field Value API](/api/cases/fields/get) to learn more about field values.

## 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 value from a case.

See the [Get Field Value API](/api/cases/fields/get) to learn more about field values.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/files/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/files/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/linked_cases/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/linked_cases/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/linked_cases/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **POST**

| Parameter | Description                                          |
| --------- | ---------------------------------------------------- |
| ids       | Array of IDs of the target cases. Max length of 150. |

| 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/metadata/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/metadata/get-key-value-pair) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/metadata/update) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/metadata/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/metadata/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Parameter        | Description                                                                                                    |
| ---------------- | -------------------------------------------------------------------------------------------------------------- |
| case_id          | The ID of the case.                                                                                            |
| note_id          | The ID of the note.                                                                                            |
| resolve_mentions | **Optional** When set to `true`, replaces mention tokens in `content` with display names. Defaults to `false`. |

```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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.                        |
| resolve_mentions | **Optional** When set to `true`, replaces mention tokens in each note's `content` with display names. Defaults to `false`. |

```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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/records/create) instead. |

## 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.

See the [Get Records API](/api/records/get) to learn more about records.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/records/get) instead. |

## 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.

See the [Get Records API](/api/records/get) to learn more about records.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/records/list) instead. |

## 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.

See the [Get Records API](/api/records/get) to learn more about records.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/records/delete) instead. |

## 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.

See the [Get Records API](/api/records/get) to learn more about records.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/subscribers/create) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/subscribers/list) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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 v1 API endpoint is deprecated and will be removed on December 1, 2026. Use the [v2 version of this endpoint](https://www.tines.com/api/cases/subscribers/delete) instead. |

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## 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.

See the [Get Task API](/api/cases/tasks/get) to learn more about case tasks.

## 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

A case task is a discrete item of work within a case — assignable to users, marked complete when done, and optionally required before case closure. See [Case tasks](https://www.tines.com/docs/cases/tasks) in the Tines docs to learn more.

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.

See the [Get Task API](/api/cases/tasks/get) to learn more about case tasks.

## 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.

See the [Get Task API](/api/cases/tasks/get) to learn more about case tasks.

## 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.

See the [Get Task API](/api/cases/tasks/get) to learn more about case tasks.

## 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.

See the [Get Task API](/api/cases/tasks/get) to learn more about case tasks.

## 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 -->

### Webhooks

#### Create

## Description

Create a case webhook for a team. A team can have a maximum of five webhooks. New webhooks are created with payload version 2.0 and notify on all event types by default.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **POST**

| Parameter     | Description                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| team_id       | The ID of the team to create the webhook for.                                                                  |
| url           | The destination URL the webhook posts to.                                                                      |
| name          | **Optional** A display name for the webhook.                                                                   |
| notifications | **Optional** An array of notification types that trigger the webhook. Defaults to `["ALL"]`. See values below. |

#### Notification types

| Value                         | Description                       |
| ----------------------------- | --------------------------------- |
| `ALL`                         | All event types (default).        |
| `CREATED`                     | Case created.                     |
| `ASSIGNED`                    | Assignee added.                   |
| `UNASSIGNED`                  | Assignee removed.                 |
| `COMMENTED`                   | Comment added.                    |
| `DELETED_COMMENT`             | Comment deleted.                  |
| `STATUS_UPDATED`              | Case status changed.              |
| `SUB_STATUS_UPDATED`          | Case sub-status changed.          |
| `SEVERITY_UPDATED`            | Priority changed.                 |
| `TAGS_ADDED`                  | Tag added.                        |
| `TAGS_REMOVED`                | Tag removed.                      |
| `FILE_ATTACHED`               | File attached.                    |
| `FILE_ATTACHED_AND_COMMENTED` | File attached with a comment.     |
| `FILE_DELETED`                | File deleted.                     |
| `FILE_PASTED`                 | File pasted inline.               |
| `CHECKLIST_ITEM_COMPLETED`    | Checklist item marked complete.   |
| `CHECKLIST_ITEM_INCOMPLETE`   | Checklist item marked incomplete. |
| `LINKED_CASE_ADDED`           | Case linked.                      |
| `LINKED_CASE_REMOVED`         | Case link removed.                |
| `METADATA_UPDATED`            | Metadata updated.                 |
| `SLA_WARNING`                 | SLA warning threshold reached.    |
| `SLA_EXCEEDED`                | SLA exceeded.                     |
| `TEAM_UPDATED`                | Case moved to a different team.   |
| `FIELD_UPDATED`               | Custom field updated.             |
| `DESCRIPTION_UPDATED`         | Case description updated.         |
| `NOTE_ADDED`                  | Note added.                       |
| `NOTE_UPDATED`                | Note updated.                     |
| `NOTE_DELETED`                | Note deleted.                     |
| `TITLE_UPDATED`               | Case title updated.               |
| `TASK_CREATED`                | Task created.                     |
| `TASK_DELETED`                | Task deleted.                     |
| `TASK_COMPLETED`              | Task marked complete.             |
| `TASK_INCOMPLETE`             | Task marked incomplete.           |
| `TASK_UPDATED`                | Task updated.                     |
| `TASK_ASSIGNED`               | Task assigned to a user.          |
| `TASK_UNASSIGNED`             | Task unassigned from a user.      |

### Sample request

```bash
curl -X POST \
  https://<tenant-domain>/api/v1/case_webhooks \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "team_id": 10,
  "url": "https://example.com/webhook",
  "name": "Case notifier"
}'
```

## Response

A successful request will return a JSON object representing the created webhook.

### Field description

| Parameter | Description                 |
| --------- | --------------------------- |
| webhook   | The created webhook object. |

#### Webhook object fields

| Parameter      | Description                                                              |
| -------------- | ------------------------------------------------------------------------ |
| id             | The webhook ID.                                                          |
| name           | The webhook name, or null if it has none.                                |
| url            | The destination URL the webhook posts to.                                |
| version_number | The payload version (1.0 or 2.0).                                        |
| notifications  | An array of event types that trigger the webhook (or `["ALL"]` for all). |
| team           | The team the webhook belongs to.                                         |

#### Team object fields

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "webhook": {
    "id": 1,
    "name": "Case notifier",
    "url": "https://example.com/webhook",
    "version_number": 2.0,
    "notifications": ["ALL"],
    "team": {
      "id": 10,
      "name": "Security"
    }
  }
}
```

<!-- cspell:enable -->

#### Get

## Description

Retrieve a single case webhook by ID.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Path Parameter | Description                        |
| -------------- | ---------------------------------- |
| id             | The ID of the webhook to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/case_webhooks/<<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 webhook.

### Field description

| Parameter | Description         |
| --------- | ------------------- |
| webhook   | The webhook object. |

#### Webhook object fields

| Parameter      | Description                                                              |
| -------------- | ------------------------------------------------------------------------ |
| id             | The webhook ID.                                                          |
| name           | The webhook name, or null if it has none.                                |
| url            | The destination URL the webhook posts to.                                |
| version_number | The payload version (1.0 or 2.0).                                        |
| notifications  | An array of event types that trigger the webhook (or `["ALL"]` for all). |
| team           | The team the webhook belongs to.                                         |

#### Team object fields

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "webhook": {
    "id": 1,
    "name": "Case notifier",
    "url": "https://example.com/webhook",
    "version_number": 2.0,
    "notifications": ["ALL"],
    "team": {
      "id": 10,
      "name": "Security"
    }
  }
}
```

<!-- cspell:enable -->

#### Update

## Description

Update a case webhook. Supply only the fields you want to change.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **PUT**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| id             | The ID of the webhook to update. |

| Parameter     | Description                                                                                                                                                                       |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| url           | **Optional** A new destination URL.                                                                                                                                               |
| name          | **Optional** A new display name. Send an empty string to clear it.                                                                                                                |
| notifications | **Optional** An array of event types that trigger the webhook. Use `["ALL"]` for all events. See the [create endpoint](create.md#notification-types) for the full list of values. |

```bash
curl -X PUT \
  https://<tenant-domain>/api/v1/case_webhooks/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
  "url": "https://example.com/updated",
  "name": "Renamed notifier",
  "notifications": ["COMMENTED", "STATUS_UPDATED"]
}'
```

## Response

A successful request will return a JSON object representing the updated webhook.

### Field description

| Parameter | Description                 |
| --------- | --------------------------- |
| webhook   | The updated webhook object. |

#### Webhook object fields

| Parameter      | Description                                                              |
| -------------- | ------------------------------------------------------------------------ |
| id             | The webhook ID.                                                          |
| name           | The webhook name, or null if it has none.                                |
| url            | The destination URL the webhook posts to.                                |
| version_number | The payload version (1.0 or 2.0).                                        |
| notifications  | An array of event types that trigger the webhook (or `["ALL"]` for all). |
| team           | The team the webhook belongs to.                                         |

#### Team object fields

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "webhook": {
    "id": 1,
    "name": "Renamed notifier",
    "url": "https://example.com/updated",
    "version_number": 2.0,
    "notifications": ["COMMENTED", "STATUS_UPDATED"],
    "team": {
      "id": 10,
      "name": "Security"
    }
  }
}
```

<!-- cspell:enable -->

#### List

## Description

Retrieve a list of case webhooks. Returns every webhook you can access, or only those for a specific team when `team_id` is supplied.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **GET**

| Query Parameter | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| team_id         | **Optional** Restrict the results to a single team.                                                 |
| 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/v1/case_webhooks?team_id=<<team_id>>' \
  -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 case webhooks.

### Field description

| Parameter | Description                               |
| --------- | ----------------------------------------- |
| webhooks  | An array of webhook objects.              |
| meta      | Object containing pagination information. |

#### Webhook object fields

| Parameter      | Description                                                              |
| -------------- | ------------------------------------------------------------------------ |
| id             | The webhook ID.                                                          |
| name           | The webhook name, or null if it has none.                                |
| url            | The destination URL the webhook posts to.                                |
| version_number | The payload version (1.0 or 2.0).                                        |
| notifications  | An array of event types that trigger the webhook (or `["ALL"]` for all). |
| team           | The team the webhook belongs to.                                         |

#### Team object fields

| Parameter | Description    |
| --------- | -------------- |
| id        | The team ID.   |
| name      | The team name. |

### Sample response

<!-- cspell:disable -->

```json
{
  "webhooks": [
    {
      "id": 1,
      "name": "Case notifier",
      "url": "https://example.com/webhook",
      "version_number": 2.0,
      "notifications": ["ALL"],
      "team": {
        "id": 10,
        "name": "Security"
      }
    },
    {
      "id": 2,
      "name": null,
      "url": "https://hooks.slack.com/services/T0/B0/XXXX",
      "version_number": 2.0,
      "notifications": ["COMMENTED", "STATUS_UPDATED"],
      "team": {
        "id": 10,
        "name": "Security"
      }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/case_webhooks?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 case webhook by ID.

See the [Get Cases API](/api/cases/get) to learn more about cases.

## Request

HTTP Method: **DELETE**

| Path Parameter | Description                      |
| -------------- | -------------------------------- |
| id             | The ID of the webhook to delete. |

```bash
curl -X DELETE \
  https://<tenant-domain>/api/v1/case_webhooks/<<id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a `204 No Content` response with an empty body.

## Records

### Create

## Description

Create a new record.

`POST /api/v1/records` is not idempotent — every successful call creates a new row, so a blind retry after a timeout or network error can produce a duplicate. See [Best practices](/api/records/best-practices) for retry and rate-limit guidance.

See the [Get Records API](/api/records/get) to learn more about records.

## Request

HTTP Method: **POST**

| Query parameter   | Description                                                                                                                                                                                                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| resolve_artifacts | **Optional** Set to the string `"false"` to have Artifacts (large text) fields in the response return a reference object containing the artifact ID instead of the full contents. Defaults to inlining the contents. See the [Get Records API](/api/records/get) for the reference-object shape. |

| Body parameter   | Description                                                                                                                                                                                                        |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| record_type_id   | Specifies the id of the record type for which the record is being created.                                                                                                                                         |
| field_values     | An array of `{ field_id, value }` 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** An array of case IDs to link to this record. At most 5 per request.                                                                                                                                   |
| parent_record_id | **Optional** The ID of the record to link as this record's parent.                                                                                                                                                 |
| test_mode        | **Optional** Set to `true` (JSON boolean) or `"true"` (string) to create the record in test mode. Defaults to `false`.                                                                                             |

```bash
curl -X POST \
  "https://<tenant-domain>/api/v1/records?resolve_artifacts=false" \
  -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 when the record was created.                   |
| updated_at    | ISO 8601 timestamp representing when the record was last updated.              |
| case_ids      | The case IDs linked to this record.                                            |
| parent_record | `{ "id": <parent_record_id> }` when the record has a parent, otherwise `null`. |
| result        | The captured data in key-value format.                                         |

### Sample response

The `records` and `result` collections always include the built-in default fields alongside any user fields you set, even if you didn't pass a value for them.

```json
{
  "id": 1973,
  "created_at": "2024-06-25T14:41:23Z",
  "updated_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": "5995", "name": "Record ID", "value": 1973 },
    { "field_id": "5994", "name": "Story name", "value": null },
    { "field_id": "5991", "name": "Timestamp", "value": "2024-06-25 15:41:23" },
    { "field_id": "5990", "name": "Updated at", "value": "2024-06-25 15:41:23" }
  ],
  "case_ids": [14, 19],
  "parent_record": null,
  "result": {
    "id": 1973,
    "updated_at": "2024-06-25T14:41:23Z",
    "case_ids": [14, 19],
    "Affected user email": "john@example.com",
    "Source": "Login alerts",
    "Description": "Suspicious login",
    "Severity": "High",
    "Record ID": 1973,
    "Story name": null,
    "Timestamp": "2024-06-25 15:41:23",
    "Updated at": "2024-06-25 15:41:23"
  }
}
```

### Get

## Description

A record is a piece of structured, queryable data captured from a story, the API, or the UI. See [Records](https://www.tines.com/docs/records/) in the Tines docs to learn more.

Retrieve a single record.

You can find the record ID in the response from the [List Records API](/api/records/list).

For opinionated guidance on building against the Records API — schema discovery, pagination, artifacts, retries, and rate limits — see [Best practices](/api/records/best-practices).

## Request

HTTP Method: **GET**

| Path parameter | Description                       |
| -------------- | --------------------------------- |
| record_id      | The ID of the record to retrieve. |

| Query parameter   | Description                                                                                                                                                                                                                  |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| record_type_id    | **Optional** The ID of the record type. When provided, the lookup is scoped to that record type — this is faster than an unscoped lookup, and returns `404` if the record ID does not belong to the given record type.       |
| 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`.                                                      |
| include_children  | **Optional** Boolean `true` or `false` value. When `true`, child records are returned with their full field data. When `false`, child records are omitted from the response. By default, only child record IDs are returned. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/records/<<record_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

For a faster lookup, 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](/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 when the record was created.                                                                                                         |
| updated_at              | ISO 8601 timestamp representing when the record was last updated.                                                                                                    |
| 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.                                                                                                                                                  |
| records                 | The captured data for the given instance of the record type.                                                                                                         |
| record_artifacts        | An array of `{ id }` objects, one per artifact attached to the record. Pass an `id` to the [Get Artifacts API](/api/records/artifacts/get) to retrieve the contents. |
| child_records           | An array of objects containing the record IDs (or full field data when `include_children=true`) for which this record is the parent.                                 |
| child_records_truncated | `true` when the child records list was capped. Only present when truncation occurred; otherwise absent from the response.                                            |
| 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",
  "updated_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
      }
    }
  ],
  "record_artifacts": [{ "id": 674623 }],
  "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",
    "Record ID": 59,
    "Timestamp": "2023-06-14 16:09:02",
    "Name": "Example",
    "Description": {
      "id": 674623,
      "is_artifact": true
    }
  }
}
```

<!-- cspell:enable -->

### Update

## Description

Updates a single record.

See the [Get Records API](/api/records/get) to learn more about records.

## Request

HTTP Method: **PUT**

| Path parameter | Description                     |
| -------------- | ------------------------------- |
| record_id      | The ID of the record to update. |

| Query parameter   | Description                                                                                                                                                                                                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| resolve_artifacts | **Optional** Set to the string `"false"` to have Artifacts (large text) fields in the response return a reference object containing the artifact ID instead of the full contents. Defaults to inlining the contents. See the [Get Records API](/api/records/get) for the reference-object shape. |
| include_children  | **Optional** Set to the string `"false"` to omit child records from the response, or `"true"` for full field data. Defaults to including child records with their full field data.                                                                                                               |

| Body parameter       | Description                                                                                                                                                                                                                                                                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| add_child_records    | **Optional** An array of record IDs to be added to the record as child records. If a record already belongs to another parent, it will be moved to the new parent (the previous parent-child relationship is removed). Adding a child record that would create a circular dependency (e.g. A is a child of B which is a child of A) will fail with a validation error. |
| remove_child_records | **Optional** An array of record IDs to remove from the record as child records.                                                                                                                                                                                                                                                                                        |
| parent_record_id     | **Optional** The ID of the record to link as this record's parent. Pass `null` to remove the existing parent. Omit the field to leave the parent unchanged. Setting a parent that would create a circular dependency (e.g. A is a child of B which is a child of A) will fail with a validation error.                                                                 |
| add_case_ids         | **Optional** An array of case IDs to be linked to the record. At most 5 per request.                                                                                                                                                                                                                                                                                   |
| remove_case_ids      | **Optional** An array of case IDs to be unlinked from the record. At most 5 per request.                                                                                                                                                                                                                                                                               |
| field_values         | **Optional** An array of `{ field_id, value }` objects. Only works if the record's record type has `editable: true`; otherwise the request fails with `422` and body `["Record is not editable"]`.                                                                                                                                                                     |

### Sample request

```bash
curl -X PUT \
  "https://<tenant-domain>/api/v1/records/<<record_id>>?resolve_artifacts=false&include_children=false" \
  -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_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 when the record was created.                                                              |
| updated_at              | ISO 8601 timestamp representing when the record was last updated.                                                         |
| 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.                                                                                                       |
| records                 | The captured data for the given instance of the record type.                                                              |
| child_records           | An array of objects containing child record data. See the [Get Records API](/api/records/get) for details.                |
| child_records_truncated | `true` when the child records list was capped. Only present when truncation occurred; otherwise absent from the response. |
| parent_record           | `{ "id": <parent_record_id> }` when the record has a parent, otherwise `null`.                                            |
| case_ids                | The 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",
  "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"
    }
  ],
  "case_ids": [14, 19],
  "result": {
    "id": 59,
    "Record 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.

If you need aggregates — counts, sums, averages, grouped breakdowns — reach for the [Query records API](/api/records/query) instead of paging through this endpoint and aggregating client-side. See [Best practices](/api/records/best-practices) for pagination, filter operator, and artifact-handling guidance.

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)

See the [Get Records API](/api/records/get) to learn more about records.

## 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** Set to the string `"true"` to filter by records created in test stories. Defaults to `false`.                                                                                                                                                                                                                                                                                                   |
| parent_ids          | **Optional** An array of parent record IDs (max 100). 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`, `IS_NONE_OF`, `CONTAINS`, `STARTS_WITH`, or `ENDS_WITH`. Note: `EQUAL`, `NOT_EQUAL`, `IS_ANY_OF`, `IS_NONE_OF`, `CONTAINS`, `STARTS_WITH`, and `ENDS_WITH` are case insensitive. A request can include at most one of the pattern filters (`CONTAINS`, `STARTS_WITH`, or `ENDS_WITH`).                                                                                                                                                                                                                                                  |
| value      | Depending on the operator, a string, number, or omitted. For `IS_ANY_OF` and `IS_NONE_OF` operators, provide an array of values (max 10 entries) instead of a single value. For `CONTAINS`, `STARTS_WITH`, and `ENDS_WITH` operators, provide a string value of at least three characters. Values are compared as strings and case-insensitively — a JSON boolean `true` stored in a record field matches the filter value `"true"` or `"TRUE"`. Store fields as strings if you plan to filter on them. Note: `IS_ANY_OF` and `IS_NONE_OF` are only available for text, text fixed enums, and number fields. `CONTAINS`, `STARTS_WITH`, and `ENDS_WITH` are only available for text and text fixed enum fields. |

### Example request with search filters

Because this is a `GET` request, `filters` must be supplied as a single JSON array string in the query string (URL-encode it if your HTTP client doesn't handle that automatically). It is not a repeated or plain query parameter. A value that isn't valid JSON returns a `400` response with the body `["Invalid filters: not JSON parsable"]`.

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=[{\"field_id\":\"5\",\"operator\":\"EQUAL\",\"value\":\"ALERT\"}]" \
  -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>>'
```

## 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](/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       | Default field on every record type: the name of the story that created this record.                                                                                                                                                                    |
| Timestamp        | Default field on every record type: the date and time the record was created.                                                                                                                                                                          |
| Record ID        | Default field on every record type: the unique ID of the record. Can be used to filter records.                                                                                                                                                        |
| Updated at       | Default field on every record type: the date and time the record was last updated.                                                                                                                                                                     |
| _(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": [
    {
      "id": 1,
      "updated_at": "2023-06-06T16:18:34Z",
      "case_ids": [1, 2],
      "parent_record": null,
      "Story name": "Create new issue",
      "Timestamp": "2023-06-06 16:18:34",
      "Record ID": 1,
      "Updated at": "2023-06-06 16:18:34",
      "Alert": "High"
    },
    {
      "id": 2,
      "updated_at": "2023-06-13T18:13:15Z",
      "case_ids": [1],
      "parent_record": null,
      "Story name": "Create new issue",
      "Timestamp": "2023-06-13 18:13:15",
      "Record ID": 2,
      "Updated at": "2023-06-13 18:13:15",
      "Alert": "Medium"
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/records?record_type_id=1&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.

See the [Get Records API](/api/records/get) to learn more about records.

## Request

HTTP Method: **DELETE**

| Path 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

An Artifact (large text) field stores extended content — such as raw logs or large data dumps — that doesn't fit inside a standard record field. See [Records](https://www.tines.com/docs/records/) in the Tines docs to learn more.

Retrieve an individual record artifact (large text field).

By default, the [GET Records API](/api/records/get) and [List Records API](/api/records/list) return only the ID of an Artifact (large text) field, not the data itself. To access the actual contents, either pass `resolve_artifacts=true` on the [GET Records API](/api/records/get) to inline the contents in the response, or make a separate request to this endpoint using the artifact 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 the 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"
}
```

### Best practices

Guidance for building with the Records API. Endpoint pages describe what each call does; this page recommends how to combine them.

See the [Get Records API](/api/records/get) for the record model.

## Reference by ID, not name

`record_type_id` and `field_id` are stable; names can be renamed at any time. Discover the schema once via the [List record types API](/api/records/record_types/list), cache field IDs by name, and refresh on `404`. The [Query API](/api/records/query) also accepts `field_name` — use it only for ad-hoc queries.

## Use /records/query for aggregations

For counts, sums, averages, or grouped breakdowns, call the [Query API](/api/records/query). It answers in one request with its own rate-limit budget. Only reach for the [List API](/api/records/list) when you need the individual rows.

## Only request what you need

- Pass `record_field_ids` on [Get](/api/records/get) or [List](/api/records/list) to return only the columns you need.
- Artifacts come back as `{ id, is_artifact: true }` references. On [Get](/api/records/get), [Create](/api/records/create), and [Update](/api/records/update) you can pass `resolve_artifacts=true` to inline the contents — only do this when you need them now. [List](/api/records/list) always returns references with no inline option, so fetch them lazily via [Get artifact](/api/records/artifacts/get) when you need the value.
- Child records come back as ID stubs. Set `include_children=true` only when you need their fields. Use `parent_ids` on [List](/api/records/list) to fetch children in one request.

## Paginate deliberately

`per_page` defaults to 20 and caps at 500. Filter and sort server-side (`filters`, `order_field_id`), follow `meta.next_page`, and bound the total pages your client will walk.

## Filter operator gotchas

- `IS_ANY_OF` / `IS_NONE_OF` accept up to 10 values — prefer them over repeated `EQUAL` requests.
- `CONTAINS`, `STARTS_WITH`, `ENDS_WITH` need three-plus characters and one pattern filter per request.
- Values compare as strings, case-insensitively — a JSON `true` matches `"true"`. Store fields as strings if you plan to filter on them.
- `ARTIFACT` and `JSON` fields cannot be filtered.

## Handle rate limits and retries

The Records API is rate-limited ([API rate limits](/api/api-rate-limits)); record and query limits are separate. On `429`, back off exponentially with jitter and a max attempts cap.

Retry safety:

- `POST /records` is not idempotent — a retry after a timeout or connection drop can create a duplicate. Dedupe on a natural key, or verify with a follow-up read before retrying.
- `PUT` and `DELETE /records/{id}` and `POST /records/query` are safe to retry.

## Use test_mode for test data

Pass `test_mode: true` on writes from staging or test workflows. Test records are excluded from live listings and aggregations unless you pass `test_mode=true` on [List](/api/records/list) or [Query](/api/records/query).

## Attaching records to cases from the record side

- On [Create](/api/records/create): `case_ids: [<id>, ...]` (max 5).
- On [Update](/api/records/update): `add_case_ids` / `remove_case_ids` (max 5).

For status, comments, fields, or Cases-only reads, use the [Cases API](/api/cases/get).

## Configure retention explicitly

Set `ttl_days`, `retention_column_name`, `max_records_limit`, and `on_limit_reached` at creation time (see [Create record type](/api/records/record_types/create)):

- Enrichment cache: `retention_column_name: "UPDATED_AT"`, `on_limit_reached: "EVICT_OLDEST"`.
- Audit trail: `retention_column_name: "CREATED_AT"`, `on_limit_reached: "REJECT"`.

## Common errors

| Status | Typical cause                                                                                                                      |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| 400    | Malformed request — `filters` not valid JSON, unknown field, disallowed operator.                                                  |
| 404    | Record, type, or field not found — **or** the token can't see it. Under-privileged tokens return `404`, not `403`.                 |
| 422    | Validation failure — `TEXT_ENUM` value not in `fixed_values`, updating a record whose record type isn't editable, circular parent. |
| 429    | Rate limited — back off.                                                                                                           |
| 504    | [Query API](/api/records/query) exceeded 30s — narrow filters or time range.                                                       |

Treat `404` on a resource you expect to exist as a scope check on the API key.

### Query

## Description

Run an aggregation query over records of a given record type. Use this endpoint to compute counts, sums, averages, and other aggregations — optionally grouped by one or more fields — without retrieving individual records.

Prefer this endpoint over paging through the [List records API](/api/records/list) and aggregating client-side — it's faster, respects a 30-second server-side timeout, and has its own rate-limit budget. See [Best practices](/api/records/best-practices) for guidance on `field_id` vs `field_name`, filters, and error handling.

See the [Get Records API](/api/records/get) to learn more about records.

## Request

HTTP Method: **POST**

URL: `https://<tenant-domain>/api/v1/records/query`

| Query parameter | Description                                                                                                                                                                         |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| test_mode       | **Optional** Set to the string `"true"` to run the query against test records only. Defaults to `false` (live records). Only effective for tenants with the change control feature. |

The request body is a JSON object with the following fields:

| Body parameter | Required | Description                                                                                                                                         |
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| record_type_id | Yes      | The numeric ID of the record type to query. The calling token must have read access to this record type.                                            |
| aggregations   | Yes      | An array of aggregation functions to compute (see below).                                                                                           |
| filters        | No       | An array of filter conditions to narrow records before aggregation (see below). All filters are ANDed together.                                     |
| group_by       | No       | An array of field references to group results by (see below). At most two entries. When omitted, you get a single row with the overall aggregation. |
| time_range     | No       | A time range to restrict which records are included (see below).                                                                                    |
| timezone       | No       | IANA timezone name applied to the query's time handling (see below).                                                                                |
| parent_ids     | No       | An array of parent record IDs (max 100). Restricts the query to first-degree children of those records.                                             |
| order_by       | No       | Sort the output rows (see below).                                                                                                                   |
| limit          | No       | Cap on the number of rows returned, between 1 and 1000.                                                                                             |

### Field identification

Every field reference in `aggregations`, `filters`, and `group_by` accepts **one of**:

| Key        | Type    | Description                                                                                                            |
| ---------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
| field_id   | Integer | The numeric ID of the record field. Stable across renames — recommended for automation.                                |
| field_name | String  | The exact, case-sensitive name of the field within the record type. More readable, but breaks if the field is renamed. |

Specify at most one of `field_id` or `field_name` per reference. `COUNT` aggregations may omit both (they count all rows).

**Tip:** Use `field_id` for stable automation that won't break if fields are renamed. Use `field_name` for ad-hoc queries and MCP integrations where readability matters.

To find field IDs and names, use the [List record types API](/api/records/record_types/list) or navigate to any record page and select **Manage record type** from the dropdown menu.

### Aggregations

Each entry in `aggregations` has:

| Key                    | Required | Description                                                                                                                      |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| fn                     | Yes      | The aggregation function: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`, or `MEDIAN`.                                                      |
| field_id or field_name | Depends  | Required for all functions except `COUNT`. `SUM`, `AVG`, `MIN`, `MAX`, and `MEDIAN` only accept `NUMBER` fields.                 |
| alias                  | No       | A name for this value in the response. Auto-generated if omitted (e.g., `count` for a bare `COUNT`, `sum_<field_id>` otherwise). |

At most five aggregations per query.

### Filters

Each entry in `filters` has:

| Key                    | Required | Description                                                                                                                                                |
| ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| field_id or field_name | Yes      | The field to filter on.                                                                                                                                    |
| operator               | Yes      | See the operator matrix below.                                                                                                                             |
| value                  | Depends  | The value to compare against. For `IS_ANY_OF` and `IS_NONE_OF`, provide an array. For `IS_EMPTY`, `IS_NOT_EMPTY`, `IS_TRUE`, and `IS_FALSE`, omit `value`. |

At most 10 filters per query. `IS_ANY_OF` and `IS_NONE_OF` arrays are capped at 10 entries each.

Operators are gated by the field's `result_type`:

| Operator                                                                         | Applicable field types                                |
| -------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `EQUAL`, `NOT_EQUAL`                                                             | `TEXT`, `TEXT_ENUM`, `NUMBER`, `TIMESTAMP`            |
| `GREATER_THAN`, `GREATER_THAN_OR_EQUAL_TO`, `LESS_THAN`, `LESS_THAN_OR_EQUAL_TO` | `NUMBER`, `TIMESTAMP`                                 |
| `IS_ANY_OF`, `IS_NONE_OF`                                                        | `TEXT`, `TEXT_ENUM`, `NUMBER`                         |
| `CONTAINS`, `STARTS_WITH`, `ENDS_WITH`                                           | `TEXT`, `TEXT_ENUM`                                   |
| `IS_EMPTY`, `IS_NOT_EMPTY`                                                       | `TEXT`, `TEXT_ENUM`, `NUMBER`, `TIMESTAMP`, `BOOLEAN` |
| `IS_TRUE`, `IS_FALSE`                                                            | `BOOLEAN`                                             |

`ARTIFACT` and `JSON` fields cannot be used in filters.

### Group-by

Each entry in `group_by` has:

| Key                    | Required | Description                                                                                                                                        |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| field_id or field_name | Yes      | The field to group by.                                                                                                                             |
| date_trunc             | No       | For timestamp fields only: `hour`, `day`, `week`, `month`, or `year`. Rolls values into calendar buckets in the query `timezone` (UTC by default). |

At most two group-by entries per query.

### Time range

The `time_range` object restricts which records are included based on when they were created. Use either a rolling preset or a custom range:

| Key                | Description                                                                               |
| ------------------ | ----------------------------------------------------------------------------------------- |
| rolling_date_range | One of: `TODAY`, `YESTERDAY`, `LAST_7_DAYS`, `LAST_31_DAYS`, `LAST_365_DAYS`, `ALL_TIME`. |
| range_start        | ISO 8601 timestamp for the start of a custom range (inclusive).                           |
| range_end          | ISO 8601 timestamp for the end of a custom range (inclusive).                             |

Use either `rolling_date_range` or a custom range (`range_start` and/or `range_end`), not both.

### Timezone

The top-level `timezone` is an optional IANA timezone name (e.g. `America/New_York`) applied to the whole query's time handling. It sets the day boundaries for `time_range` rolling presets, the `date_trunc` group-by buckets, and the interpretation of custom `time_range` timestamps without an offset. It defaults to the server's timezone (UTC on Tines cloud) when omitted.

A custom timestamp that includes an explicit offset (for example a trailing `Z`, or `+05:00`) is an absolute instant, so `timezone` does not change how it is interpreted. Bucket boundaries fall on the chosen zone's calendar, and each bucket label carries that zone's offset. An unknown timezone is rejected with a validation error.

### Order-by

The `order_by` object controls how result rows are sorted:

| Key       | Required | Description                                                                                 |
| --------- | -------- | ------------------------------------------------------------------------------------------- |
| column    | Yes      | The name of a column to sort by — either a group-by field's name or an aggregation `alias`. |
| direction | No       | `ASC` or `DESC`. Defaults to `ASC`.                                                         |

### Example request

```bash
curl --proto '=https' --tlsv1.2 \
  -X POST \
  "https://<tenant-domain>/api/v1/records/query" \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "record_type_id": 123,
    "aggregations": [
      { "fn": "COUNT", "alias": "total" },
      { "fn": "AVG", "field_id": 8, "alias": "avg_score" }
    ],
    "filters": [
      { "field_id": 4, "operator": "EQUAL", "value": "blue" }
    ],
    "group_by": [
      { "field_id": 1, "date_trunc": "day" },
      { "field_name": "Color" }
    ],
    "time_range": { "rolling_date_range": "LAST_31_DAYS" },
    "timezone": "America/New_York",
    "order_by": { "column": "total", "direction": "DESC" },
    "limit": 50
  }'
```

### Example request using field_name

```bash
curl --proto '=https' --tlsv1.2 \
  -X POST \
  "https://<tenant-domain>/api/v1/records/query" \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "record_type_id": 123,
    "aggregations": [
      { "fn": "COUNT", "alias": "total" },
      { "fn": "SUM", "field_name": "Amount", "alias": "total_amount" }
    ],
    "filters": [
      { "field_name": "Status", "operator": "EQUAL", "value": "open" }
    ],
    "group_by": [
      { "field_name": "Category" }
    ]
  }'
```

### Rate limit

This endpoint has its own rate limit, separate from the general records rate limit. Please be aware that both request count and request duration are throttled.

## Response

A successful request returns a JSON object containing the aggregation results.

### Field description

| Field | Description                                           |
| ----- | ----------------------------------------------------- |
| rows  | Array of result rows, each a flat object (see below). |
| meta  | Object containing metadata about the query result.    |

Each object in `rows` is a flat map of column names to values:

- **Group-by columns** are keyed by the field's name (e.g., `"Color"`, `"Timestamp"`). Timestamp values bucketed with `date_trunc` are ISO 8601 strings whose offset reflects the query `timezone` (a `Z` suffix by default, otherwise the zone's offset such as `-04:00`).
- **Aggregation columns** are keyed by the `alias` you specified (or auto-generated). `COUNT` values are integers. Numeric aggregates (`SUM`, `AVG`, `MIN`, `MAX`, `MEDIAN`) are returned as strings to preserve precision.

The `meta` object contains:

| Field                | Description                                                                                                          |
| -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| row_count            | The number of rows returned.                                                                                         |
| truncated            | `true` when no `limit` was specified and the result hit the 1000-row system cap. Always `false` when `limit` is set. |
| columns.group_by     | Array of column names in `rows` that come from the `group_by` fields.                                                |
| columns.aggregations | Array of column names in `rows` that come from `aggregations`.                                                       |

### Sample response — grouped query

```json
{
  "rows": [
    {
      "Timestamp": "2026-05-01T00:00:00-04:00",
      "Color": "red",
      "total": 12,
      "avg_score": "70.0"
    },
    {
      "Timestamp": "2026-05-01T00:00:00-04:00",
      "Color": "blue",
      "total": 5,
      "avg_score": "64.0"
    }
  ],
  "meta": {
    "row_count": 2,
    "truncated": false,
    "columns": {
      "group_by": ["Timestamp", "Color"],
      "aggregations": ["total", "avg_score"]
    }
  }
}
```

### Sample response — single-number query (no group_by)

```json
{
  "rows": [
    {
      "total": 173,
      "avg_score": "62.4"
    }
  ],
  "meta": {
    "row_count": 1,
    "truncated": false,
    "columns": {
      "group_by": [],
      "aggregations": ["total", "avg_score"]
    }
  }
}
```

## Errors

Errors are returned as a JSON object with an `error` field containing `type` and `message`, along with an appropriate HTTP status code:

| Status | Meaning                                                                                                    |
| ------ | ---------------------------------------------------------------------------------------------------------- |
| 400    | Invalid query — malformed body, unknown field, disallowed operator, or validation failure.                 |
| 404    | Record type not found, or the calling token does not have access to the specified record type.             |
| 504    | Query timeout — the query exceeded the 30-second time limit. Narrow your filters or reduce the time range. |

### Example error response

```json
{
  "error": {
    "type": "bad_request",
    "message": "record_type_id is required and must be an integer"
  }
}
```

## Limits

| Limit                      | Value                                              |
| -------------------------- | -------------------------------------------------- |
| Statement timeout          | 30 seconds                                         |
| Maximum rows returned      | 1000 (indicated by `meta.truncated` when exceeded) |
| Maximum aggregations       | 5 per query                                        |
| Maximum group-by fields    | 2 per query                                        |
| Maximum filters            | 10 per query                                       |
| Maximum `IS_ANY_OF` values | 10 entries per filter                              |
| Rate limit                 | 200 requests per minute (per IP and tenant)        |

**No caching:** every query runs fresh against the database. If you're calling this endpoint in a loop, add a delay between requests to stay within the 200 requests per minute rate limit.

## Tips

- Use `order_by` + `limit` together to get "top N" results (e.g., top five categories by count).
- When `meta.truncated` is `true`, narrow your query with filters or a tighter time range to see all results.
- The `MEDIAN` function computes the 50th percentile, which is useful for skewed data where `AVG` can be misleading.
- `field_name` is resolved by exact, case-sensitive match within the record type. If you rename a field, queries using `field_name` will break — use `field_id` for rename-proof automation.

### Record types

#### Create

## Description

Create a new record type.

Retention behavior is set at creation time via `ttl_days`, `retention_column_name`, `max_records_limit`, and `on_limit_reached`. See [Best practices](/api/records/best-practices) for how to combine these depending on whether you're modelling an enrichment cache, a workflow buffer, or an audit trail.

See the [Get Record Type API](/api/records/record_types/get) to learn more about record types.

## Request

HTTP Method: **POST**

| Body 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. A record type can have at most 50 custom fields. Each object accepts:

| Field property | Description                                                                                                                                                           |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name           | The field name.                                                                                                                                                       |
| result_type    | The field's data type: `TEXT`, `NUMBER`, `TIMESTAMP`, `BOOLEAN`, `TEXT_ENUM` (text with a predefined set of values), `JSON` (100k limit), or `ARTIFACT` (large text). |
| fixed_values   | Required for `TEXT_ENUM`, otherwise omit. Array of allowed values (max 100 entries).                                                                                  |
| description    | **Optional** A short description of the 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": "Alerts",
      "description": "Alerts from external sources",
      "team_id": "<<META.team.id>>",
      "fields": [
        { "name": "Severity", "result_type": "TEXT_ENUM", "fixed_values": ["low", "medium", "high"] },
        { "name": "Description", "result_type": "TEXT", "description": "Free-form description of the alert" }
      ],
      "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 built-in default record fields.                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "Alerts",
  "description": "Alerts from external sources",
  "team_id": 123,
  "editable": true,
  "ttl_days": 3,
  "retention_column_name": "UPDATED_AT",
  "on_limit_reached": "EVICT_OLDEST",
  "max_records_limit": 5000,
  "record_fields": [
    {
      "id": 1,
      "name": "Severity",
      "description": null,
      "result_type": "TEXT_ENUM",
      "fixed_values": ["low", "medium", "high"]
    },
    {
      "id": 2,
      "name": "Description",
      "description": "Free-form description of the alert",
      "result_type": "TEXT",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 3,
      "name": "Record ID",
      "description": null,
      "result_type": "NUMBER",
      "fixed_values": []
    },
    {
      "id": 4,
      "name": "Story name",
      "description": null,
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 5,
      "name": "Timestamp",
      "description": null,
      "result_type": "TIMESTAMP",
      "fixed_values": []
    },
    {
      "id": 6,
      "name": "Updated at",
      "description": null,
      "result_type": "TIMESTAMP",
      "fixed_values": []
    }
  ]
}
```

#### Get

## Description

A record type defines the schema for a set of records — the fields to store and how they behave. See [Records](https://www.tines.com/docs/records/) in the Tines docs to learn more.

Retrieve a single record type.

## Request

HTTP Method: **GET**

| Path 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 built-in default record fields.                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "Alerts",
  "description": "Alerts from external sources",
  "team_id": 123,
  "editable": true,
  "ttl_days": 4,
  "retention_column_name": "CREATED_AT",
  "on_limit_reached": "REJECT",
  "max_records_limit": null,
  "record_fields": [
    {
      "id": 1,
      "name": "Severity",
      "description": null,
      "result_type": "TEXT",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 2,
      "name": "Record ID",
      "description": null,
      "result_type": "NUMBER",
      "fixed_values": []
    },
    {
      "id": 3,
      "name": "Story name",
      "description": null,
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 4,
      "name": "Timestamp",
      "description": null,
      "result_type": "TIMESTAMP",
      "fixed_values": []
    },
    {
      "id": 5,
      "name": "Updated at",
      "description": null,
      "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.

See the [Get Record Type API](/api/records/record_types/get) to learn more about record types.

## Request

HTTP Method: **PUT**

| Path parameter | Description                          |
| -------------- | ------------------------------------ |
| record_type_id | The ID of the record type to update. |

| Body parameter        | Description                                                                                                                                                                                                                                                                                                                                                      |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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-level `description` can also be updated.                                                                                                                                                                                            |
| field_ids_to_delete   | **Optional** An array of field IDs to delete. Use this to explicitly remove fields from the record type. Built-in default fields cannot be deleted.                                                                                                                                                                                                              |

### 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`. A record type can have at most 50 custom fields.

To delete fields, use the `field_ids_to_delete` parameter with an array of field IDs to remove.

Each field object accepts:

| Field property | Description                                                                                                                                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id             | Required to update an existing field; omit to create a new one.                                                                                                                                               |
| name           | The field name.                                                                                                                                                                                               |
| result_type    | The field's data type: `TEXT`, `NUMBER`, `TIMESTAMP`, `BOOLEAN`, `TEXT_ENUM`, `JSON` (100k limit), or `ARTIFACT`. Changing `result_type` on an existing field is only allowed between `TEXT` and `TEXT_ENUM`. |
| fixed_values   | Required for `TEXT_ENUM`, otherwise omit. Array of allowed values (max 100 entries).                                                                                                                          |
| description    | **Optional** A short description of the field.                                                                                                                                                                |

```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 built-in default record fields.                                                                                                      |

### Sample response

```json
{
  "id": 1,
  "name": "Updated record type name",
  "description": "Updated description of the record type",
  "team_id": 123,
  "editable": true,
  "ttl_days": 7,
  "retention_column_name": "UPDATED_AT",
  "on_limit_reached": "EVICT_OLDEST",
  "max_records_limit": 5000,
  "record_fields": [
    {
      "id": 1,
      "name": "Updated Column Name",
      "description": null,
      "result_type": "TEXT_ENUM",
      "fixed_values": ["Option A", "Option B", "Option C"]
    },
    {
      "id": 6,
      "name": "New Column",
      "description": null,
      "result_type": "NUMBER",
      "fixed_values": []
    }
  ],
  "default_record_fields": [
    {
      "id": 2,
      "name": "Record ID",
      "description": null,
      "result_type": "NUMBER",
      "fixed_values": []
    },
    {
      "id": 3,
      "name": "Story name",
      "description": null,
      "result_type": "TEXT",
      "fixed_values": []
    },
    {
      "id": 4,
      "name": "Timestamp",
      "description": null,
      "result_type": "TIMESTAMP",
      "fixed_values": []
    },
    {
      "id": 5,
      "name": "Updated at",
      "description": null,
      "result_type": "TIMESTAMP",
      "fixed_values": []
    }
  ]
}
```

#### List

## Description

Retrieve a list of record types.

See the [Get Record Type API](/api/records/record_types/get) to learn more about 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 built-in default record fields.                                                                                                      |

### Sample response

<!-- cspell:disable -->

```json
{
  "record_types": [
    {
      "id": 1,
      "name": "Alerts",
      "description": "Alerts from external sources",
      "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": "Severity",
          "description": "Text enum, one of low/medium/high",
          "result_type": "TEXT_ENUM",
          "fixed_values": ["low", "medium", "high"]
        }
      ],
      "default_record_fields": [
        {
          "id": 2,
          "name": "Record ID",
          "description": null,
          "result_type": "NUMBER",
          "fixed_values": []
        },
        {
          "id": 3,
          "name": "Story name",
          "description": null,
          "result_type": "TEXT",
          "fixed_values": []
        },
        {
          "id": 4,
          "name": "Timestamp",
          "description": null,
          "result_type": "TIMESTAMP",
          "fixed_values": []
        },
        {
          "id": 5,
          "name": "Updated at",
          "description": null,
          "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": 1
  }
}
```

<!-- cspell:enable -->

#### Delete

## Description

Delete a record type.

This also deletes all records of this type, their artifacts, and all record views built on the record type.

See the [Get Record Type API](/api/records/record_types/get) to learn more about record types.

## Request

HTTP Method: **DELETE**

| Path 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. See [Records](https://www.tines.com/docs/records/) in the Tines docs to learn more.

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`. |
| url                    | Relative URL path to access the record view.                                    |
| record_report_group_id | Integer ID of the record report group this view belongs to.                     |

### 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",
      "url": "/teams/14/records/5/views/1",
      "record_report_group_id": 5
    },
    {
      "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",
      "url": "/teams/14/records/6/views/2",
      "record_report_group_id": 6
    }
  ],
  "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.

See the [List Record Views API](/api/records/record_views/list) to learn more about record views.

## Request

HTTP Method: **DELETE**

| Path 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.

See the [List Record Views API](/api/records/record_views/list) to learn more about record views.

## 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.               |

To recreate a record view in another team or tenant, pass the entire export object (including `schema_version`) as the `data` parameter to the [Import Record View API](/api/records/record_views/import).

Each entry in `elements` has an `id`, and each entry in `layout` has a matching `i` value that references the element it lays out. Preserve both when passing the export to the import endpoint.

### 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": "bar_chart",
        "axes": [
          {
            "direction": "X",
            "record_field_name": "priority"
          }
        ],
        "aggregation": "COUNT",
        "rolling_date_range": "LAST_7_DAYS",
        "range_start": null,
        "range_end": null,
        "filters": {
          "stories": [],
          "fields": [
            {
              "value": "open",
              "operator": "EQUAL",
              "name": "status"
            }
          ]
        },
        "order": {
          "field": null,
          "direction": "DESC"
        },
        "color_palette": "colorful",
        "options": {},
        "record_fields": [],
        "test_mode": false
      }
    ],
    "layout": [
      {
        "i": "a1b2c3d4e5f6",
        "h": 4,
        "w": 6,
        "x": 0,
        "y": 0
      }
    ]
  }
}
```

<!-- cspell:enable -->

#### Import

## Description

Import a record view. The import data should be obtained from the [export endpoint](/api/records/record_views/export).

See the [List Record Views API](/api/records/record_views/list) to learn more about record views.

## 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/records/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. |

The `data` value is the full response body from the [Export Record View API](/api/records/record_views/export), including `schema_version`. Any record types and story slugs referenced in the export must already exist in the destination team — they are resolved by name and are not created automatically.

```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,
      "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": "bar_chart",
            "axes": [{ "direction": "X", "record_field_name": "priority" }],
            "aggregation": "COUNT",
            "rolling_date_range": "LAST_7_DAYS",
            "range_start": null,
            "range_end": null,
            "filters": { "stories": [], "fields": [] },
            "order": { "field": null, "direction": "DESC" },
            "color_palette": "colorful",
            "options": {},
            "record_fields": [],
            "test_mode": false
          }
        ],
        "layout": [{ "i": "a1b2c3d4e5f6", "h": 4, "w": 6, "x": 0, "y": 0 }]
      }
    }
  }'
```

## 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.

### SMTP configuration

#### Get

## Description

Retrieve the SMTP configuration used by your self-hosted tenant to send email.

This endpoint is only available on self-hosted tenants. If no configuration has been set through the API, the response returns the SMTP settings from the tenant environment.

## Request

HTTP Method: **GET**

### Sample request

```bash
curl -X GET \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/smtp \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request will return a JSON object describing the current SMTP configuration. The password is never returned. Use `password_set` to check whether a password is configured.

When `source` is `environment`, the response uses the same shape as API-managed settings, but values are loaded from the tenant environment instead of the database. Configuration warnings are only returned for API-managed database settings.

### Field description

| Parameter              | Description                                                                                                                                                                                                                                        |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| address                | SMTP server address.                                                                                                                                                                                                                               |
| port                   | SMTP server port.                                                                                                                                                                                                                                  |
| domain                 | Domain sent during the SMTP greeting.                                                                                                                                                                                                              |
| user_name              | User name used for SMTP authentication.                                                                                                                                                                                                            |
| auth_type              | Authentication mode. Supported values are `plain`, `login`, and `cram_md5`.                                                                                                                                                                        |
| encryption_mode        | Encryption mode. Supported values are `none`, `tls`, `starttls_auto`, and `starttls_strict`.                                                                                                                                                       |
| verify_ssl_certificate | Boolean flag indicating whether TLS certificates are verified.                                                                                                                                                                                     |
| ca_path                | Path to a directory of CA certificates on the self-hosted installation.                                                                                                                                                                            |
| ca_file                | Path to a CA certificate file on the self-hosted installation.                                                                                                                                                                                     |
| open_timeout           | SMTP connection open timeout in seconds.                                                                                                                                                                                                           |
| read_timeout           | SMTP connection read timeout in seconds.                                                                                                                                                                                                           |
| source                 | Indicates where the current settings were loaded from. `database` means the tenant is using settings saved through this API. `environment` means no API-managed configuration exists, so Tines is using SMTP settings from the tenant environment. |
| password_set           | Boolean flag indicating whether a password is configured.                                                                                                                                                                                          |
| warnings               | Optional object describing configuration warnings by field.                                                                                                                                                                                        |

### Configuration notes

Use `encryption_mode` to control how Tines secures the SMTP connection:

- `none`: Connect without TLS or STARTTLS.
- `tls`: Use direct TLS from the start of the SMTP connection. This is typically used with port `465`.
- `starttls_auto`: Use STARTTLS when the SMTP server advertises support for it. If the server does not advertise STARTTLS, continue without TLS.
- `starttls_strict`: Require STARTTLS. The connection fails if the SMTP server does not advertise STARTTLS.

Port `587` is typically used with `starttls_auto` or `starttls_strict`.

STARTTLS modes begin with a plain SMTP connection, then upgrade the connection with the STARTTLS command. If the server advertises STARTTLS but TLS negotiation or certificate verification fails, the connection fails.

When `verify_ssl_certificate` is `false`, Tines does not verify the SMTP server's TLS certificate. Default system certificates and any `ca_path` or `ca_file` values are not used to verify trust. When both `ca_path` and `ca_file` are provided, `ca_file` takes precedence.

Use `auth_type` to control how Tines authenticates to the SMTP server:

- `plain`: Send the user name and password together in one SMTP authentication command.
- `login`: Send the user name and password in separate SMTP authentication steps.
- `cram_md5`: Use challenge-response authentication instead of sending the password directly.

### Sample response

```json
{
  "address": "smtp.example.com",
  "port": 587,
  "domain": "example.com",
  "user_name": "smtp-user",
  "auth_type": "plain",
  "encryption_mode": "starttls_auto",
  "verify_ssl_certificate": true,
  "ca_path": null,
  "ca_file": null,
  "open_timeout": 30,
  "read_timeout": 30,
  "source": "database",
  "password_set": true
}
```

## Errors

This endpoint is only allowed for self-hosted tenants.

| Status | Meaning                                          |
| ------ | ------------------------------------------------ |
| 403    | The endpoint was called on a Tines cloud tenant. |

#### Update

## Description

Create or replace the SMTP configuration used by your self-hosted tenant to send email.

This endpoint is only available on self-hosted tenants. The request replaces the full SMTP configuration and does not update individual fields, so include every value you want to keep. Optional fields that are omitted are cleared, except `open_timeout` and `read_timeout`, which default to `30` seconds when omitted.

Because the password is never returned by the GET endpoint, it must be included whenever you save a configuration that uses SMTP authentication. Existing passwords are not preserved automatically during replacement.

## Request

HTTP Method: **PUT**

### Field description

| Parameter              | Required | Description                                                                                              |
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| address                | Yes      | SMTP server address. Maximum length: 255 characters.                                                     |
| port                   | Yes      | SMTP server port. Must be an integer from `1` to `65535`.                                                |
| domain                 | No       | Domain sent during the SMTP greeting. Maximum length: 255 characters.                                    |
| user_name              | No       | User name used for SMTP authentication. Maximum length: 255 characters.                                  |
| password               | No       | Password used for SMTP authentication. Maximum length: 1024 characters.                                  |
| auth_type              | No       | Authentication mode. Supported values are `plain`, `login`, and `cram_md5`.                              |
| encryption_mode        | Yes      | Encryption mode. Supported values are `none`, `tls`, `starttls_auto`, and `starttls_strict`.             |
| verify_ssl_certificate | Yes      | Boolean flag indicating whether TLS certificates are verified.                                           |
| ca_path                | No       | Path to a directory of CA certificates on the self-hosted installation. Maximum length: 4096 characters. |
| ca_file                | No       | Path to a CA certificate file on the self-hosted installation. Maximum length: 4096 characters.          |
| open_timeout           | No       | SMTP connection open timeout in seconds. Must be an integer from `1` to `120`. Default is `30`.          |
| read_timeout           | No       | SMTP connection read timeout in seconds. Must be an integer from `1` to `120`. Default is `30`.          |

If `auth_type` is set, `user_name` and `password` are required. If `auth_type` is omitted or `null`, Tines does not attempt SMTP authentication. If either `user_name` or `password` is set without `auth_type`, both values must be provided.

### Configuration notes

Use `encryption_mode` to control how Tines secures the SMTP connection:

- `none`: Connect without TLS or STARTTLS.
- `tls`: Use direct TLS from the start of the SMTP connection. This is typically used with port `465`.
- `starttls_auto`: Use STARTTLS when the SMTP server advertises support for it. If the server does not advertise STARTTLS, continue without TLS.
- `starttls_strict`: Require STARTTLS. The connection fails if the SMTP server does not advertise STARTTLS.

Port `587` is typically used with `starttls_auto` or `starttls_strict`.

STARTTLS modes begin with a plain SMTP connection, then upgrade the connection with the STARTTLS command. If the server advertises STARTTLS but TLS negotiation or certificate verification fails, the connection fails.

When `verify_ssl_certificate` is `false`, Tines does not verify the SMTP server's TLS certificate. Default system certificates and any `ca_path` or `ca_file` values are not used to verify trust. When both `ca_path` and `ca_file` are provided, `ca_file` takes precedence.

Use `auth_type` to control how Tines authenticates to the SMTP server:

- `plain`: Send the user name and password together in one SMTP authentication command.
- `login`: Send the user name and password in separate SMTP authentication steps.
- `cram_md5`: Use challenge-response authentication instead of sending the password directly.

### Sample request

```bash
curl -X PUT \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/smtp \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
       "address": "smtp.example.com",
       "port": 587,
       "domain": "example.com",
       "user_name": "smtp-user",
       "password": "smtp-password",
       "auth_type": "plain",
       "encryption_mode": "starttls_auto",
       "verify_ssl_certificate": true,
       "ca_path": null,
       "ca_file": null,
       "open_timeout": 30,
       "read_timeout": 30
      }'
```

## Response

A successful request will return an empty response with a `200` status code.

If the configuration is saved but has warnings, the request will return a `200` status code with a JSON object describing the warnings.

### Sample response with warnings

```json
{
  "warnings": {
    "ca_path": ["Ca path will be ignored because ca_file takes precedence"]
  }
}
```

## Errors

Errors are returned as a JSON object, along with an appropriate HTTP status code:

| Status | Meaning                                                                          |
| ------ | -------------------------------------------------------------------------------- |
| 403    | The endpoint was called on a Tines cloud tenant.                                 |
| 422    | Invalid SMTP configuration, including missing required fields or invalid values. |

### Example validation error response

```json
{
  "errors": {
    "port": ["Port can't be blank"],
    "encryption_mode": ["Encryption mode can't be blank"],
    "verify_ssl_certificate": ["Verify ssl certificate must be true or false"]
  }
}
```

#### Delete

## Description

Delete the SMTP configuration set through the API for your self-hosted tenant.

This endpoint is only available on self-hosted tenants. This deletes only the API-managed database configuration and does not change environment variables. After the configuration is deleted, the tenant falls back to the SMTP settings from the environment.

## Request

HTTP Method: **DELETE**

### Sample request

```bash
curl -X DELETE \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/smtp \
  -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. The same response is returned when there is no API-managed SMTP configuration to delete.

## Errors

This endpoint is only allowed for self-hosted tenants.

| Status | Meaning                                          |
| ------ | ------------------------------------------------ |
| 403    | The endpoint was called on a Tines cloud tenant. |

### 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. Use the current tenant's URL for same-tenant sync.                               |
| destination_tenant_api_key | **Optional** Tines API key used to publish to the destination tenant. Required only when syncing to a remote 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.                                                                |
| manual_sync_only           | **Optional** When `true`, stories are only synced on manual trigger.                                                 |

### Sync to a remote tenant

```bash
curl -X POST \
  --proto '=https' --tlsv1.2 \
  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://remote-tenant.tines.com",
    "destination_tenant_api_key": "<<CREDENTIAL.remote_tenant_api_key>>",
    "destination_team_id": 42,
    "team_id": 7,
    "story_ids": [9981, 9982],
    "manual_sync_only": false
  }'
```

### Sync within the same tenant

When syncing to a different team on the same tenant, set `destination_tenant_url` to your own tenant URL and omit `destination_tenant_api_key`.

```bash
curl -X POST \
  --proto '=https' --tlsv1.2 \
  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_team_id": 42,
    "team_id": 7,
    "story_ids": [9981, 9982],
    "manual_sync_only": false
  }'
```

## 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.                     |
| manual_sync_only              | Whether the sync destination only syncs on manual trigger.                   |
| 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,
    "manual_sync_only": false,
    "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
      }
    ]
  }
}
```

#### Update

## Description

Update an existing sync destination. Use this to rotate the API key, change which stories are synced, or toggle manual-only sync.

The source team, destination team, destination tenant URL, and whether the sync is remote or same-tenant cannot be changed after creation. To change these, delete the sync destination and create a new one.

## Request

HTTP Method: **PUT**, **PATCH**

| Body parameter             | Description                                                                                                                                                   |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| destination_tenant_api_key | **Optional** Tines API key used to publish to the destination tenant. When omitted, the existing key is preserved.                                            |
| story_ids                  | **Optional** Array of story IDs (from the current tenant) to sync. Pass `[]` to remove all subscriptions. When omitted, existing subscriptions are preserved. |
| manual_sync_only           | **Optional** When `true`, stories are only synced on manual trigger. When omitted, the existing value is preserved.                                           |

```bash
curl -X PUT \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{
    "destination_tenant_api_key": "<<CREDENTIAL.tines_api_key>>",
    "story_ids": [9981, 9982, 9983],
    "manual_sync_only": true
  }'
```

## Response

A successful request returns status `200` and the updated sync destination object.

### 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.                     |
| manual_sync_only              | Whether the sync destination only syncs on manual trigger.                   |
| 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,
    "manual_sync_only": true,
    "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
      },
      {
        "story": { "id": 9983, "name": "Incident Response" },
        "status": "requires_sync",
        "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 \
  --proto '=https' --tlsv1.2 \
  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.                     |
| manual_sync_only              | Whether the sync destination only syncs on manual trigger.                   |
| 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,
      "manual_sync_only": false,
      "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. By default, synced stories on the destination tenant are also deleted. Set `keep_stories` to `true` to preserve them.

## Request

HTTP Method: **DELETE**

| URL parameter | Description                           |
| ------------- | ------------------------------------- |
| id            | ID of the sync destination to delete. |

| Body parameter | Required | Description                                                                                                                                                                                                                                                                                                                           |
| -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| keep_stories   | No       | Boolean. When `false` (default), synced stories are deleted from the destination. When `true`, stories are detached from the sync source and remain on the destination as independent stories. Detached stories that would cause the destination tenant to exceed its limits, depending on pricing model, are automatically disabled. |

```bash
curl -X DELETE \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

To preserve synced stories on the destination tenant:

```bash
curl -X DELETE \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24 \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>' \
  -d '{"keep_stories": true}'
```

## Response

On success a `204 No Content` status is returned and the body is empty. When `keep_stories` is `false` (default), story cleanup on the destination tenant happens asynchronously.

#### 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 \
  --proto '=https' --tlsv1.2 \
  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
}
```

#### Show

## Description

Retrieve a single sync destination by ID.

## Request

HTTP Method: **GET**

```bash
curl -X GET \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/sync_destinations/<<sync_destination_id>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a JSON object containing the sync destination.

### 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.                     |
| manual_sync_only              | Whether the sync destination only syncs on manual trigger.                   |
| 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": 23,
    "destination_tenant_url": "https://<tenant-domain>",
    "destination_team_id": 42,
    "manual_sync_only": false,
    "origin_team_id": 7,
    "subscriptions": [
      {
        "story": {
          "id": 9981,
          "name": "Example"
        },
        "status": "synced",
        "error_message": null
      }
    ]
  }
}
```

#### Sync a single story

## Description

Trigger an immediate push of a single subscribed story to the destination tenant.

## Request

HTTP Method: **POST**

| URL Parameter | Description                                                          |
| ------------- | -------------------------------------------------------------------- |
| id            | ID of the sync destination.                                          |
| story_id      | ID of the story to sync (as returned in `subscriptions[].story.id`). |

```bash
curl -X POST \
  --proto '=https' --tlsv1.2 \
  https://<tenant-domain>/api/v1/admin/sync_destinations/24/story/12/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
{
  "story_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.                                           |
| email          | An email address can be supplied in place of the user ID. |

| Query Parameter | Description                              |
| --------------- | ---------------------------------------- |
| include         | **Optional** Include: `team_memberships` |

```bash
curl --proto '=https' --tlsv1.2 -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 retrieve a user by email address:

```bash
curl --proto '=https' --tlsv1.2 -X GET \
  https://<tenant-domain>/api/v1/admin/users/<<user_email>> \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Or, to include `team_memberships`:

```bash
curl --proto '=https' --tlsv1.2 -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`                                                                  |
| search          | **Optional** Case-insensitive substring match against user name or email. May return multiple users. |
| include         | **Optional** Include: `team_memberships`                                                             |

```bash
curl --proto '=https' --tlsv1.2 -X GET \
  https://<tenant-domain>/api/v1/admin/users \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

Or, to search users by name or email:

```bash
curl --proto '=https' --tlsv1.2 -X GET \
  https://<tenant-domain>/api/v1/admin/users?search=alice \
  -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.

## AI providers

### List

## Description

Retrieve the AI providers and models available to the authenticated user.

Admins receive all AI providers configured for the tenant. Non-admin users receive providers owned by one of their teams, shared with one of their teams, or available to the entire tenant. The response includes enabled and disabled providers.

## Request

HTTP Method: **GET**

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/ai_providers \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a JSON object containing an array of AI providers. Each provider includes the models that can be selected when configuring Tines AI features.

### Field description

| Parameter         | Description                                                                                                     |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| ai_providers      | Array of AI provider objects visible to the authenticated user.                                                 |
| guid              | Unique identifier of the AI provider.                                                                           |
| enabled           | Whether the AI provider is enabled.                                                                             |
| name              | The configured name of the AI provider, or its default provider name when no custom name is set.                |
| provider_type     | Provider service. One of `ANTHROPIC`, `OPEN_AI`, `AWS_BEDROCK`, `ORACLE_COHERE`, `MISTRAL`, or `GOOGLE_VERTEX`. |
| provisioning_type | How the provider is provisioned. One of `customer_provisioned` or `tines_provisioned`.                          |
| models            | Array of models available through the provider.                                                                 |
| display_name      | Display name of the model.                                                                                      |
| api_model_id      | Model identifier used when configuring Tines AI features through the API.                                       |
| description       | Description of the model, or `null` when no description is available.                                           |
| cost_tier         | Relative model cost. One of `LOW`, `MEDIUM`, or `HIGH`, or `null` when no cost tier is available.               |
| legacy            | Whether the model is a legacy model.                                                                            |

### Sample response

```json
{
  "ai_providers": [
    {
      "guid": "2c6c29e7-0f57-42b6-b7f7-663a45299f69",
      "enabled": true,
      "name": "My Anthropic",
      "provider_type": "ANTHROPIC",
      "provisioning_type": "customer_provisioned",
      "models": [
        {
          "display_name": "Sonnet 5",
          "api_model_id": "claude-sonnet-5",
          "description": "Best combination of speed and intelligence for a wide range of tasks",
          "cost_tier": "MEDIUM",
          "legacy": false
        }
      ]
    }
  ]
}
```

## AI usage

### List

## Description

Retrieve aggregated AI usage for the tenant, including token counts, credits consumed, and cost in US dollars. Results can be filtered by time range, team, model, provider, or feature, and optionally grouped by one of these dimensions.

Admins see usage across the entire tenant. Non-admin users see usage from teams they belong to, plus usage rows not attributed to any team.

## Request

HTTP Method: **GET**

| Parameter     | Description                                                                                                                                                                                                      | Required | Type    |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- |
| relative_date | **Optional** Predefined time window relative to now, for example `today`, `yesterday`, `7 days`, `30 days`, `3 months`, or `1 year`. Defaults to `30 days`. Cannot be combined with `start_date` and `end_date`. | No       | String  |
| start_date    | **Optional** ISO 8601 timestamp for the start of a custom time window. Must be paired with `end_date`.                                                                                                           | No       | String  |
| end_date      | **Optional** ISO 8601 timestamp for the end of a custom time window. Must be paired with `start_date`, must be after `start_date`, and the window cannot exceed one year.                                        | No       | String  |
| team_id       | **Optional** Only return usage attributed to the specified team. Non-admin users requesting usage for a team they don't belong to will receive a zeroed response.                                                | No       | Integer |
| model_id      | **Optional** Only return usage attributed to the specified model.                                                                                                                                                | No       | String  |
| provider_id   | **Optional** Only return usage attributed to the specified provider.                                                                                                                                             | No       | Integer |
| feature       | **Optional** Only return usage attributed to the specified feature: `workbench`, `workbench_storyboard`, `ai_agents`, or `other`.                                                                                | No       | String  |
| group_by      | **Optional** Break the response into per-bucket groups along one dimension: `model`, `provider`, `team`, `feature`, or `day`. When omitted, only the `totals` are returned.                                      | No       | String  |

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/ai_usage?relative_date=30%20days&group_by=model" \
  -H 'content-type: application/json' \
  -H 'Authorization: Bearer <<CREDENTIAL.tines_api_key>>'
```

## Response

A successful request returns a JSON object summarizing aggregate AI usage over the requested window. When `group_by` is supplied, the response also includes a `groups` array, with one bucket per grouping key.

### Field description

| Parameter | Description                                                                                          |
| --------- | ---------------------------------------------------------------------------------------------------- |
| range     | The resolved time window. Contains `start` and `end` as ISO 8601 timestamps.                         |
| totals    | Aggregate usage across the entire window. See [Bucket fields](#bucket-fields).                       |
| group_by  | Echoes the `group_by` parameter when provided. Omitted from the response when `group_by` is not set. |
| groups    | An array of usage buckets, one per grouping key. Omitted when `group_by` is not set.                 |

#### Bucket fields

Each bucket in `totals` and `groups` shares the same shape. Group buckets additionally include the grouping key(s) for that bucket.

| Parameter                  | Description                                                                                                                                                                                      |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| input_tokens               | Total input tokens sent to the model in this bucket.                                                                                                                                             |
| cached_read_input_tokens   | Total input tokens served from the model's prompt cache.                                                                                                                                         |
| cached_write_input_tokens  | Total input tokens written to the model's prompt cache.                                                                                                                                          |
| output_tokens              | Total output tokens generated by the model.                                                                                                                                                      |
| credits_used               | Total Tines AI credits consumed.                                                                                                                                                                 |
| billed_cost                | Total cost as an object with `currency` (for example, `usd`) and `amount` (a string formatted to two decimal places, for example, `"184.20"`).                                                   |
| usage_count                | Number of individual AI usage events rolled up into this bucket.                                                                                                                                 |
| model_id                   | Present when `group_by=model`: the model identifier for this bucket.                                                                                                                             |
| provider_id, provider_name | Present when `group_by=provider`: the provider identifier and display name for this bucket.                                                                                                      |
| team_id, team_name         | Present when `group_by=team`: the team identifier and name. `team_id` is `null` for usage not attributed to a team.                                                                              |
| feature                    | Present when `group_by=feature`: one of `workbench`, `workbench_storyboard`, `ai_agents`, or `other`. The `other` bucket covers usage that doesn't map to one of these customer-facing features. |
| day                        | Present when `group_by=day`: the ISO 8601 date for this bucket.                                                                                                                                  |

### Sample response (ungrouped)

```json
{
  "range": {
    "start": "2026-04-12T00:00:00Z",
    "end": "2026-05-12T00:00:00Z"
  },
  "totals": {
    "input_tokens": 1245000,
    "cached_read_input_tokens": 420000,
    "cached_write_input_tokens": 80000,
    "output_tokens": 312000,
    "credits_used": 18420,
    "billed_cost": {
      "currency": "usd",
      "amount": "184.20"
    },
    "usage_count": 1284
  }
}
```

### Sample response (grouped by model)

```json
{
  "range": {
    "start": "2026-04-12T00:00:00Z",
    "end": "2026-05-12T00:00:00Z"
  },
  "totals": {
    "input_tokens": 1245000,
    "cached_read_input_tokens": 420000,
    "cached_write_input_tokens": 80000,
    "output_tokens": 312000,
    "credits_used": 18420,
    "billed_cost": {
      "currency": "usd",
      "amount": "184.20"
    },
    "usage_count": 1284
  },
  "group_by": "model",
  "groups": [
    {
      "input_tokens": 845000,
      "cached_read_input_tokens": 380000,
      "cached_write_input_tokens": 60000,
      "output_tokens": 210000,
      "credits_used": 12300,
      "billed_cost": {
        "currency": "usd",
        "amount": "123.00"
      },
      "usage_count": 812,
      "model_id": "claude-sonnet-4-6"
    },
    {
      "input_tokens": 400000,
      "cached_read_input_tokens": 40000,
      "cached_write_input_tokens": 20000,
      "output_tokens": 102000,
      "credits_used": 6120,
      "billed_cost": {
        "currency": "usd",
        "amount": "61.20"
      },
      "usage_count": 472,
      "model_id": "gpt-4o"
    }
  ]
}
```

### Time windows

- Use `relative_date` for a rolling predefined window. The window ends at the current time and starts the corresponding duration in the past. Accepted values are `today`, `yesterday`, or a count followed by `day`, `week`, `month`, or `year` (singular or plural), for example `7 days` or `1 year`.
- Use `start_date` and `end_date` for a custom window. Both must be valid ISO 8601 timestamps, `end_date` must be later than `start_date`, and the window cannot exceed one year.
- `relative_date` and the `start_date`/`end_date` pair are mutually exclusive — supplying both returns a 400 response.

### Grouping

When `group_by` is omitted, only `range` and `totals` are returned. When provided, the response additionally contains:

- `group_by`: echoes the requested dimension.
- `groups`: one bucket per distinct grouping key, each with the same usage fields as `totals` plus the key(s) for that bucket.

A `null` grouping key is preserved — for example, when grouping by `team`, usage not attributed to a team appears as a bucket with `team_id: null`. When grouping by `feature`, usage that doesn't map to a customer-facing feature is collected under a bucket with `feature: "other"`.

## Apps

### Get

## Description

Retrieve a single app.

## Request

HTTP Method: **GET**

| Path Parameter | Description                    |
| -------------- | ------------------------------ |
| app_id         | The ID of the app to retrieve. |

```bash
curl -X GET \
  https://<tenant-domain>/api/v1/apps/<<app_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 app.

### Field description

| Parameter         | Description                                                                                       |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| id                | The app ID.                                                                                       |
| guid              | Unique identifier of the app.                                                                     |
| name              | The app name.                                                                                     |
| description       | A user-defined description of the app.                                                            |
| emoji             | The emoji associated with the app.                                                                |
| created_at        | ISO 8601 timestamp representing date and time the app was created.                                |
| updated_at        | ISO 8601 timestamp representing date and time the app was last updated.                           |
| team              | Object containing the `id` and `name` of the team the app belongs to.                             |
| published_version | Object containing the `id` of the published version, or `null` if the app has not been published. |

### Sample response

```json
{
  "id": 42,
  "guid": "abc123def456",
  "name": "Phishing triage",
  "description": "Triages reported phishing emails.",
  "emoji": "📧",
  "created_at": "2026-05-14T10:00:00Z",
  "updated_at": "2026-05-14T11:30:00Z",
  "team": { "id": 7, "name": "Security" },
  "published_version": { "id": 101 }
}
```

### List

## Description

Retrieve a list of apps visible to the authenticated user.

## 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/apps \
  -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 apps visible to the authenticated user.

### Field description

| Parameter         | Description                                                                                       |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| id                | The app ID.                                                                                       |
| guid              | Unique identifier of the app.                                                                     |
| name              | The app name.                                                                                     |
| description       | A user-defined description of the app.                                                            |
| emoji             | The emoji associated with the app.                                                                |
| created_at        | ISO 8601 timestamp representing date and time the app was created.                                |
| updated_at        | ISO 8601 timestamp representing date and time the app was last updated.                           |
| team              | Object containing the `id` and `name` of the team the app belongs to.                             |
| published_version | Object containing the `id` of the published version, or `null` if the app has not been published. |

### Sample response

```json
{
  "apps": [
    {
      "id": 42,
      "guid": "abc123def456",
      "name": "Phishing triage",
      "description": "Triages reported phishing emails.",
      "emoji": "📧",
      "created_at": "2026-05-14T10:00:00Z",
      "updated_at": "2026-05-14T11:30:00Z",
      "team": { "id": 7, "name": "Security" },
      "published_version": { "id": 101 }
    }
  ],
  "meta": {
    "current_page": "https://<tenant-domain>/api/v1/apps?per_page=20&page=1",
    "previous_page": null,
    "next_page": null,
    "next_page_number": null,
    "per_page": 20,
    "pages": 1,
    "count": 1
  }
}
```

## 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 and customers on dedicated stacks in cloud (not available for customers on multitenant stacks).

## 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 conversation, including metadata, aggregate AI usage, and the full transcript.

### Field description

| Parameter         | Description                                                                                                                                                           |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| guid              | The conversation GUID.                                                                                                                                                |
| title             | The conversation title.                                                                                                                                               |
| source            | The source of the conversation (for example, `tines` or `slack`).                                                                                                     |
| status            | The current status of the conversation: `in_progress`, `complete`, `cancelled`, or `failed`.                                                                          |
| conversation_type | The type of conversation: `workbench`, `task_agent`, `chat_agent`, or `workbench_storyboard`.                                                                         |
| source_type       | The type of the resource the conversation is linked to (for example, `Story`), or `null` if it has no source.                                                         |
| source_id         | The ID of the resource the conversation is linked to, or `null` if it has no source.                                                                                  |
| favorited         | Whether the conversation is favorited by the user.                                                                                                                    |
| creator_id        | The ID of the user or agent that created the conversation.                                                                                                            |
| creator_type      | The type of entity that created the conversation: `User` or `Agent`.                                                                                                  |
| created_at        | ISO 8601 timestamp when the conversation was created.                                                                                                                 |
| updated_at        | ISO 8601 timestamp when the conversation was last updated.                                                                                                            |
| tool_uses_count   | The total number of tool invocations performed during the conversation.                                                                                               |
| credits_used      | The total number of Tines AI credits consumed by the conversation.                                                                                                    |
| billed_cost       | The total cost of the conversation, as an object with `currency` (for example, `usd`) and `amount` (a string formatted to two decimal places, for example, `"0.01"`). |
| usage             | An object summarizing aggregate model token usage. See [usage fields](#usage-fields).                                                                                 |
| steps             | An array of conversation messages including user, assistant, agent, and tool interactions.                                                                            |

#### Usage fields

| Parameter                 | Description                                                                   |
| ------------------------- | ----------------------------------------------------------------------------- |
| input_tokens              | The total number of input tokens sent to the model across all messages.       |
| output_tokens             | The total number of output tokens generated by the model across all messages. |
| cached_read_input_tokens  | The total number of input tokens served from the model's prompt cache.        |
| cached_write_input_tokens | The total number of input tokens written to the model's prompt cache.         |
| model_ids                 | The list of model IDs used across the conversation.                           |

### Sample response

```json
{
  "guid": "ca68403e-5594-42a4-bec7-879b0b417a83",
  "title": "Weather in Dublin",
  "source": "tines",
  "status": "complete",
  "conversation_type": "workbench",
  "source_type": null,
  "source_id": null,
  "favorited": false,
  "creator_id": 12345,
  "creator_type": "User",
  "created_at": "2025-06-20T08:25:30Z",
  "updated_at": "2025-06-20T08:30:15Z",
  "tool_uses_count": 1,
  "credits_used": 250,
  "billed_cost": {
    "currency": "usd",
    "amount": "2.50"
  },
  "usage": {
    "input_tokens": 1840,
    "output_tokens": 412,
    "cached_read_input_tokens": 0,
    "cached_write_input_tokens": 0,
    "model_ids": ["claude-sonnet-4-6"]
  },
  "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 AI conversations across all surfaces, including workbench, AI agents, and story authoring.

## 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       | Integer |
| conversation_type | **Optional** Filter by conversation type: `workbench`, `task_agent`, `chat_agent`, or `workbench_storyboard`. An invalid value returns a 400 response. | No       | String  |
| created_after     | **Optional** Only return conversations created at or after this ISO 8601 timestamp                                                                     | No       | String  |
| created_before    | **Optional** Only return conversations created at or before this ISO 8601 timestamp                                                                    | 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 |

When both `created_after` and `created_before` are provided, `created_after` must be earlier than `created_before`. Invalid or out-of-order timestamps return a 400 response.

```bash
curl -X GET \
  "https://<tenant-domain>/api/v1/workbench?per_page=20&page=1&search=weather&favorited=true&created_after=2025-06-01T00:00:00Z" \
  -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 AI conversations.

### Field description

| Parameter         | Description                                                                                                                                                            |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| conversations     | Array of conversation objects.                                                                                                                                         |
| guid              | The conversation GUID.                                                                                                                                                 |
| title             | The conversation title.                                                                                                                                                |
| source            | The source of the conversation (for example, `tines` or `slack`).                                                                                                      |
| conversation_type | The type of conversation: `workbench`, `task_agent`, `chat_agent`, or `workbench_storyboard`.                                                                          |
| source_type       | The type of the resource the conversation is linked to (for example, `Story`), or `null` if it has no source.                                                          |
| source_id         | The ID of the resource the conversation is linked to, or `null` if it has no source.                                                                                   |
| favorited         | Whether the conversation is favorited by the user.                                                                                                                     |
| creator_id        | The ID of the user or agent that created the conversation.                                                                                                             |
| creator_type      | The type of entity that created the conversation: `User` or `Agent`.                                                                                                   |
| created_at        | ISO 8601 timestamp when the conversation was created.                                                                                                                  |
| updated_at        | ISO 8601 timestamp when the conversation was last updated.                                                                                                             |
| tool_uses_count   | The total number of tool invocations performed during the conversation.                                                                                                |
| credits_used      | The total number of Tines AI credits consumed by the conversation.                                                                                                     |
| billed_cost       | The total cost of the conversation, as an object with `currency` (for example, `usd`) and `amount` (a string formatted to two decimal places, for example, `"12.00"`). |

### Sample response

```json
{
  "conversations": [
    {
      "guid": "ca68403e-5594-42a4-bec7-879b0b417a83",
      "title": "Reset user password",
      "source": "tines",
      "conversation_type": "workbench",
      "source_type": null,
      "source_id": null,
      "favorited": true,
      "creator_id": 12345,
      "creator_type": "User",
      "created_at": "2025-06-20T08:25:30Z",
      "updated_at": "2025-06-20T08:30:15Z",
      "tool_uses_count": 3,
      "credits_used": 1200,
      "billed_cost": {
        "currency": "usd",
        "amount": "12.00"
      }
    },
    {
      "guid": "b8f2c1a3-9876-4321-a1b2-c3d4e5f6789a",
      "title": "Phishing triage agent",
      "source": "slack",
      "conversation_type": "chat_agent",
      "source_type": null,
      "source_id": null,
      "favorited": false,
      "creator_id": 12345,
      "creator_type": "User",
      "created_at": "2025-06-19T14:20:10Z",
      "updated_at": "2025-06-19T16:45:22Z",
      "tool_uses_count": 7,
      "credits_used": 4250,
      "billed_cost": {
        "currency": "usd",
        "amount": "42.50"
      }
    }
  ],
  "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
- **conversation_type**: Only return conversations of the given type. Must be one of `workbench`, `task_agent`, `chat_agent`, or `workbench_storyboard`; an invalid value returns a 400 response.
- **created_after** / **created_before**: ISO 8601 timestamps that bound the conversation creation date. They can be used independently or together.

# Tines Explained

## 21 day trial of AI in Tines

You can start your trial of [AI in Tines, the AI agent action and Workbench,](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents) in Tines, by opting in **[here](https://hq.tines.io/pages/agents_trial/).**

### **What’s this trial for?**

This trial lets you explore **[AI Agent](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents)** **action type** and [Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench) commitment free. It's an excellent way to experiment with the features.

### **How long does the trial last?**

**21 days** from the day you opt in.

### **Who can access the trial?**

The trial is available to **paying Tines customers** (it is not available for Community Edition).

### **Can I extend the trial?**

You sure can - just reach out and we’ll sort it out for you.

### **Does the trial include additional AI credits?**

For the most realistic trial of AI in Tines, we did not adjust your credits. This way you can effectively estimate whether or not you'll need more credits to implement AI in your workflows. You can check your credit usage by following [these steps.](https://explained.tines.com/en/articles/9369092-how-ai-usage-and-ai-credits-work-in-tines)

### **Why are we offering this trial?**

To give you a **low-friction way to test powerful new features**, and help identify how they can support your workflows

### **Who can I contact with questions?**

Your Tines point of contact, or simply open the messenger in the bottom right and get in touch. We're always available to chat.

### **Can I see how it works?**

Sure thing. Check out the demo below. 👇🏼

[Watch video](https://fast.wistia.net/embed/iframe/yen5oyj0qb)

## Abnormal Security Authentication Guide

Abnormal provides total protection against the widest range of email attacks including phishing, malware, ransomware, social engineering, executive impersonation, supply chain compromise, internal account compromise, spam, and graymail.

## First, get a Abnormal Security Access Token

1.  Login to your Abnormal Security account at [https://portal.abnormalsecurity.com/](https://portal.abnormalsecurity.com/)
    
2.  Click "Settings" and then click "Integrations"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943210643/3bb2ce094943a026b25a1dee/image.png?expires=1784851200&signature=d27ccb49d98b775fced046a348950875772819d3828ada3c4fdde5a31dc63664&req=fSQkFMh%2Bm4VcFb4V1XW4gRR7Z7AuSMa%2FmbS2%2F9%2BHTws9zX5ZfOKMbC0ZBvmR%0A%2Fruv8qx%2FHOdtrcLG5POtFfE7cg%3D%3D%0A)
    
      
    ​
    
3.  Under "Additional Integrations", click "+ Connect"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943211532/d00d1b68fa444aeef0aea76b/image.png?expires=1784851200&signature=48f3ea1786bf2bec9e5277ea6f9363a7018c99220aba92d8c084a6efac76fe67&req=fSQkFMh%2FmIJdFb4V1XW4gaXsqyU42vYuXUcQJ8XrQta5qMoWwy22%2BnV6t2A9%0A9JdAVMlEK7L%2FZsTQW0dnvAJtrg%3D%3D%0A)
    
      
    ​
    
4.  Copy your Access Token key to a text editor and add any required IP addresses to "IP Safelist"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943213153/9a619c9ccf644693cf63e3ae/image.png?expires=1784851200&signature=32bdc4b3d85da4a8c1e1523ba310b9d4e19a3c5c901aee3290cdbd19ee46a0ae&req=fSQkFMh9nIRcFb4V1XW4gbDHwPeaOkExGafxQhTNpBdFPX%2Bew4wPctmTMVCd%0A2GFOTp0mAu1EZo%2BVlqFakEPUpg%3D%3D%0A)
    

## Lastly, create a Abnormal Security credential in Tines

**Abnormal Security connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Abnormal Security" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/abnormal-security).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Abnormal Security** **stories in the [story library](https://www.tines.com/library?s=abnormal%20security&view=all)**

## AbuseIPDB Authentication Guide

AbuseIPDB is a project dedicated to helping combat the spread of hackers, spammers, and abusive activity on the internet.

## First, get an AbuseIPDB API key

1.  Navigate to [https://www.abuseipdb.com/](https://www.abuseipdb.com/) and click "Sign up" to create a new account. Existing AbuseIPDB users can click "Login"
    
    ![](https://downloads.intercomcdn.com/i/o/750438952/3247ac797d3e950ad39d723f/Screen+Shot+2023-03-27+at+4.47.28+PM.png?expires=1784851200&signature=85cdcd0704370b0ff450b312114d1da689606f8b676ac6526b7cf19896d19fce&req=cyUnEsp2lIRdFb4V1XW4gfE9dPSFc5rmUgQ2ioP2B7Ck%2BIqdu0lJ3TL%2FwxPD%0AgFAa4RDNY8140nq9TYJowlf%2BZw%3D%3D%0A)
    
2.  In the middle panel, click "API" and then "Create Key"
    
    ![](https://downloads.intercomcdn.com/i/o/773084665/6a75ef3216454aed164093e0/image.png?expires=1784851200&signature=7c5ae37ea7f60111a69ab877fa6910eb9ea6571559a598fcd954401cb1a20a68&req=cyckFsF6m4daFb4V1XW4gfjNKBUU2ed0a0cYIkiiaHNyBRh6Oag%2BsAPFMNBU%0AInC9YCyBBColnX4dGiZdR7iqSg%3D%3D%0A)
    
3.  Give the key a name and then copy the key
    

### Then, create an AbuseIPDB credential in Tines

**AbuseIPDB connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "AbuseIPDB" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the AbuseIPDB connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the AbuseIPDB credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/abuseipdb).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of AbuseIPDB stories in the [story library](https://www.tines.com/story-library?s=abuseipdb)

## Accessing audit logs

To access the audit logs, select settings from your menu and choose 'Audit logs' in the 'Monitoring' section.

You may have to scroll through the settings options on the left.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810270865/c96a03202e8aa557cf3583fcbec4/CleanShot+2025-11-03+at+11_07_30%402x.png?expires=1784851200&signature=5b785ed57086934acc9ce8e2777ef8b3785db6dd0c584de429e25f699b4bdcc7&req=dSgmFst5nYlZXPMW3nq%2BgbCHOBTyivBBpie5sNmgauhTRwfMkL80jn0SbhLm%0AQcBS3zrT%2F9tZPG0g3ogOKEK5wFk%3D%0A)

From here, you can filter this list by user and operation. Click "Download CSV" to start an immediate download. You can also export audit logs directly to Amazon S3 buckets by clicking the settings icon next to "Download CSV".

Audit logs in Tines are stored in UTC timezone and ISO8601 format

e.g. 2023-04-21T10:30:02+00:00

> **Note:** To access audit logs, you must be a tenant admin. Audit logs is part of the [Security Essentials](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging) Tines package.

## Adaptive Shield Authentication Guide

Adaptive Shield is a popular Security Posture Management solution that notifies security teams of potential risks in their SaaS environment and attempts to fix them.

## First, get an Adaptive Shield API key

1.  Login to your Adaptive Shield account at [https://dashboard.adaptive-shield.com](https://dashboard.adaptive-shield.com/)
    
2.  Click on the user menu and select "User Profile"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/879827748/c3b0d6273d301ef5fd09c664/image.png?expires=1784851200&signature=a3d11aed04cb15f9cacabdd48fe55c9a457f35c98fd337eb5fb3376ee3bc8e8f&req=fCcuHst5moVXFb4V1XW4gX2rZ2sx7cnojiYmOk8ZLIzKsDowOnE7Av2IY83i%0AJ%2FxayadbrVgxlxrzQ8NlSVIDIQ%3D%3D%0A)
    
      
    ​
    
3.  Click the "API" tab and then click "+ Generate new key"
    
4.  Name the key and click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/879828593/07615dfe9f6a6484e32cb64c/image.png?expires=1784851200&signature=35b6bab835e6dc49bb2d569ea6f47b487e6335f2508ec2258d9420f50b78b6e5&req=fCcuHst2mIhcFb4V1XW4gUb9cvLTA%2BHFeMgbWTOzBaimGvLbyy%2FwhRTV%2FoAI%0Ad9xP8A4NEajEedPPYn%2B6b8i%2Fqw%3D%3D%0A)
    
      
    ​
    
5.  Copy the API key to a text editor
    

## Then, create an Adaptive Shield credential in Tines

**Adaptive Shield connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Adaptive Shield" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Adaptive Shield connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Adaptive Shield credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    4.  **Domains:** \*.adaptive-shield.com
        
    5.  **Metadata**:
        
        ```
        {
          "domain": "https://api.adaptive-shield.com or https://eu.api.adaptive-shield.com",
          "account_id": "your account id"
        }
        ```
        
    6.  **Access:** What other teams can also use the API
        
    
      
    ​
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/adaptive-shield).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Adaptive Shield** **stories in the [story library](https://www.tines.com/library?s=adaptive%20shield&view=all)**

## Adding stories to the Tines story library

Tines story library has over a thousand pre-built examples of Tines in action to accelerate your story building or fuel your imagination on what to build next. These are built by the folks at Tines, and builders who use Tines every day.

> **Note:** You can tell us your ideas for a story, or if you would like your own story featured in our library, submit a copy of it [here](https://www.tines.com/library/stories/?modal=request-story).

When you're ready to submit a story, here are some tips on how to prepare your story for upload to the library:

-   Be clear
    
-   Edit your story
    
-   Make it easy to follow
    

## Be clear

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078040731/2128b0a15141d4757705601c55a0/Be+clear.png?expires=1784786400&signature=871d83fbb54ced3b73719b7b5376159f4e85b5d96dc51c4b8f525b84e5985eac&req=diAgHsl6nYZcWPMW3nq%2BgaiQtmDtQfowo8vdOHNrRwPZgOzZqTTmLqppTG%2FI%0AvhpLl2naLJktjBDQnf7cYmX2dLk%3D%0A)

### **Story name**

The story name should make clear what the story does, without being verbose.

You should start with a verb, and highlight the tool(s) used. If a lot of tools are used, this can be changed to “multiple tools” or similar.

**Examples of good story names:**

```
- Triage emails with Sublime Security in Slack
- Update Slack Status based on Zoom Meetings
- Reserve IPs for new hosts in Infoblox NIOS
- Respond to alerts from various tools with the AI Agent action
```

### **Description**

The description panel should outline the main purpose of the story and include a brief description of the story, giving a high level overview of what it does. This should be written in plain text.

**Examples of good descriptions:**

```
Receive security alerts from various products and agentically create a case with enriched IOCs and suggested actions. Use agents triggered by case actions to complete the actions and update the case.
```

* * *

```

Automate Pull Request reviews in GitHub using the AI Agent action. When a review is requested, the system analyzes the changes, generates relevant comments, and posts them for the submitter. Optionally, an AI chatbot can be used to perform a comprehensive review.
```

* * *

```

Enrich a company when it is added to an Airtable database. Receive a webhook notification when a new record is added and fill out the remaining fields with web searches powered by Tavily.
```

## Edit your story

Before exporting your story, make sure to review it so you’re not sharing any internal details or personal information.

### Monitoring

Many times, the monitoring tab is auto-populated with an email which can lead to spam emails from users who later import a story. If this isn't cleared before submission, it will be removed before the story gets published to the library.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078041887/fa139bb60f54db80649228fc4168/Monitoring.png?expires=1784786400&signature=7eb5caa85547160b6a43ceffd319e560b825201ef962149028bdaecb5f5b643c&req=diAgHsl6nIlXXvMW3nq%2BgS2jjxg0UdskY7qnH3Ei8YL6FBNKN92h0vS8EgoX%0APahoV695g0lImY5%2BmI4Kv1Awkh0%3D%0A)

### Scheduling

Ensure no actions have scheduling enabled so stories aren’t running without a user’s explicit knowledge. If stories should run on a schedule, remember to add a note to the storyboard so people know to enable this.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078042640/5874efccf61ae6424f64a2c5a532/Scheduling.png?expires=1784786400&signature=9a08d4daa1372681440f104ef5102253900f832190df08765ea341393091c81b&req=diAgHsl6n4dbWfMW3nq%2Bgb4Hs%2FJBdMEsRIZNSjcber0Br8iTklN3N0iLbJIk%0AnFnazx%2Bfy9hT6CvtMU%2FadkEabAE%3D%0A)

### **Security first**

Never hard-code emails, IDs, or credentials. Use Resources or "Pills" so the story is ready for anyone to use immediately with their story’s data.

### **Action naming & cleanup**

-   **Be descriptive:** Give every action a name that explains its purpose (e.g., "Send a Slack message" instead of just "Message").
    
-   **HTTP requests:** Include the tool name in the action title.
    
-   **cURL cleanup:** If you paste a cURL command, remember to delete the default "Created from cURL command" description.
    

### **Action simplicity & logic**

The goal is to make automations easy to follow and modify.

-   **Keep formulas simple:** If a formula is getting too complex, split the functions into LOCAL fields or multiple actions.
    
-   **Use placeholders:** When using "Pills" for values like domains or IDs, add a note next to the action to guide the user on what they need to update.
    
-   **Credential metadata:** For domains or IDs specifically linked to credentials, set those values directly in the credentials metadata.
    

* * *

## Make it easy to follow

The layout of your story is important to help new users understand the overall flow and order of events.

### Use the space

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078043562/9ed6b77d8cc16d959f09778f0c60/Use+the+Space.png?expires=1784786400&signature=29c39d1e7824f9696303633d309f241505a2dd1d47d62e4094849eecdee49acb&req=diAgHsl6noRZW%2FMW3nq%2BgQwYzKlS1BrEd7ndfdpuVl1sv4B37%2F%2BHPASNJ6vI%0AiKBL5789th3nRI2sh7N7X97Gn1U%3D%0A)

Avoid the congestion of elements on the storyboard. The connectors between the actions are enough for our builders to know what is next in the flow. See the heavy congestion to the left.

### Let it flow

Think about a grid system, row by row, column by column, everything should slot straight into place and on the right level, actions and notes included.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078044010/68120bbd29b09990db99f1a25a05/Let+it+flow.png?expires=1784786400&signature=9eaa5f41241e5d80c46c7791b287c93a715675bb6d0e8ba66b074bb33e3de0ab&req=diAgHsl6mYFeWfMW3nq%2Bgbf6KYwKhq1QU3RupwdODqMJMebfyaQE5BlCfXo5%0AOtOk9WWCRyyeHdBVwu%2BYMA731nY%3D%0A)

With more actions and notes, stories can certainly become overwhelming, especially in a larger story.

### Floating singular actions

Some stories have actions that have a singular mechanic in what they do and live alongside a story. We recommend that you sign post those with a note, in order to draw attention and for new builders to understand this isn't a mistake in the story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078044540/65a8f8393b4493d4e084e8b5e962/Floating+singular+actions.png?expires=1784786400&signature=a5f6af2fdf18781c080e7f711a24bc1fa3c10abecfb27af50c9de4b1241763de&req=diAgHsl6mYRbWfMW3nq%2BgcC%2ByMMXamd3E%2Bog%2FjGqwOzmM0PJeo06ADa74%2B%2F3%0A9xMdcg0%2Fb0FJhiUMCB3Cwxg4sac%3D%0A)

### Notes

Every story should have at least two notes present:

-   Overview
    
-   Requirements
    

**Overview**

The overview note should explain the purpose of an automation and how it works. It should be more in-depth than the story metadata description.

**Requirements**

Every story has it's own level of requirements that could contain: Credentials and Resources. These can be kept in the same note with each item under the sub heading. Checkboxes work nicely here for out requirement too. Ensure details for credential metadata is also provided under each credential.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078045297/b01cd9d637c7f821a2ee270552ff/image.png?expires=1784786400&signature=10601d46ecac77300f1f3bdcd54654c197161e50ebb910b99536e4292b788865&req=diAgHsl6mINWXvMW3nq%2BgZ4Oiy%2FZh0i6FZi29am2rTvLtVyhtsI48q%2FNIZ9v%0ALq9k8%2F1Rc8Sw5C7NqpSMHa%2FbPFI%3D%0A)

### Note alignment & visual hierarchy

We recommend aligning the top edge of a note with top edge of an action. It seems like a small thing, but it will help your grid feel a lot more concise and from a zoomed out perspective, feel less messy.

The words in notes hold a lot of the untold nuances of our stories. If things aren't clear, it can be super challenging especially while learning a new tool. It can be very tempting to write large notes to get everything down, but this can cause visual chaos and could be slightly overwhelming to builders.

Instead:

1.  Lead with a captivating title - Use H1 for your Leading header and for breaks in sections within a note
    
2.  Use toggle headings (H1, H2, or H3) to collapse and expand sections — keeping long notes organized without the clutter. Type `/` in any note to add a toggle heading.
    
3.  Use body text for paragraphs
    
4.  Highlight words with bold text
    
5.  For formula and code use the code snippet markdown
    
6.  When using links be sure to give it a title instead of the url itself to clean it up.

## Additional cases resources

There are many resources available to help you understand more about cases. Starting with our explained articles and going deeper into library examples, product docs, and API docs.

Below are quick links to each of those resources.

* * *

### Product docs

Clear descriptions of the cases features and how to use them. This is useful when you are wanting to create, manage, or get into the technical aspects of cases.

[Explore our product docs](https://www.tines.com/docs/records-cases/cases)

### API docs

Developer documentation on ways to interact with our cases API endpoint.

[Read the API docs](https://www.tines.com/api/cases/create)

### Library

The library houses workflows that include Tines cases interacting with other systems. This is a great resource for both learning how to embed cases into your processes and get inspiration for your next build.

[Explore the library](https://www.tines.com/library/cases-records)

### Additional articles and blogs

We also have the following resources to support your understanding cases in Tines.

-   [Introduction to cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases)
    
-   [How to create a case](https://explained.tines.com/en/articles/7974460-how-to-create-a-case-in-tines)
    
-   [Cases website page](https://www.tines.com/product/cases) to understand the high-level value
    
-   [Transitioning to Tines cases for automation tasks](https://www.tines.com/blog/transitioning-to-tines-cases) from Michael Tolan in Tines Labs
    

> **Please note**: Cases is an add-on feature for all pricing plans. We offer a 45-day trial for all paid plans. Talk to your account team to learn more.

### Video: Introduction to Cases

A high-level video describing the feature generally.

[Watch video](https://www.youtube.com/embed/3ZD8UBx8wIc?rel=0)

## AI Agent action FAQs

## Overview

**What is the AI Agent action?**

The **AI Agent action** (also known as the AI Agent, or simply, agent) is an evolution of the AI action, providing even more AI capabilities in Tines. The AI Agent action includes two modes:

-   **Task** mode autonomously thinks and responds to prompts in a workflow using multiple, integrated tools.
    
-   **Chat** mode allows builders to create AI-powered pages that external end-users can interact with, powered by, and specific to the tools added to the AI Agent action.
    

**What is the difference between a deterministic workflow and an agentic one?**

A deterministic workflow follows a linear set of actions, resulting in a predictable outcome with consistent timing. An agentic workflow does not follow a linear path but acts autonomously using a prompt to achieve its end goal. The agentic workflow can adapt to its environment and access tools to complete tasks. The AI Agent action utilizes the agentic approach.

**When is it advised to use the AI Agent action in my workflow?**

The best time to use the AI Agent action in your workflow is when you are looking to do:

-   **Adaptive decision-making:** The AI Agent action makes decisions based on data it encounters without requiring every possible scenario to be laid out in advance.
    
-   **Contextual adaptation:** The AI Agent action pulls context from multiple sources and adapts its behavior or responses based on its understanding of a situation, rather than following a linear flow.
    
-   **AI chat for your organization:** Bring the power of the AI Agent action directly to the end-user via **Chat** mode.
    

**How does the AI Agent action relate to the AI action?**

The AI Agent action is an evolution of our original AI action. The AI Agent action now offers two modes, **Task** and **Chat**, depending on your requirements for user interaction.

**How does the AI Agent action's Chat mode compare to a Workbench chat?**

-   The AI-powered chat built from an AI Agent action differs from a Workbench chat. **Chat** mode is an AI-powered page that external end-users can access. The chat is defined by the agent it was built on, including a pre-determined end-state based on the prompt it was given.
    
-   Workbench is meant for builders within Tines with specific permissions, and cannot be accessed by end-users. A Workbench conversation is intended to be an open-ended dialogue between the LLM and the user. Ultimately, it’s up to the user to decide what the final outcome should be.
    

* * *

## Availability

**Do I have to be a Tines customer to use the AI Agent action?**

Yes, you must be a Tines customer to use the AI Agent action. The AI Agent action is available on Business and Enterprise plans and is not available on Tines community tenants. For more details, check out [our pricing and packaging article](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).

**Is the AI Agent action available for self-hosted Tines tenants?**

The AI Agent action is available for self-hosted tenants.

**What is the minimum level of user access needed within the tenant to build with the AI Agent action?**

The minimum [default role](https://www.tines.com/docs/admin/teams/#roles) to build with the AI Agent action is the Editor role. If your pricing plan includes [custom roles](https://www.tines.com/docs/admin/user-administration/custom-roles/), the minimum permissions needed are the story authoring permissions (Stories - **Create, Run, and Update**).

**What stories and credentials can I access within my tenant from an AI Agent action?**

Within the AI Agent action, you can specify the tools that it accesses during setup via the `+ Add tool` option. Just like our templates feature, you can connect the tools to your desired [credentials](https://www.tines.com/docs/credentials/). The agent will not attempt to access any stories, tools, or credentials outside what is specified in its configuration.

* * *

## Functionality

**What types of abilities can the AI Agent action execute?**

The AI Agent action has two modes: **Task** mode and **Chat** mode. **Task** mode is fully autonomous, as the agent executes a monologue conversation with itself to achieve specific tasks. **Chat** mode offers an AI chat interface for end-users, where the agent attempts to achieve a certain goal and obtain information from the user based on its configured prompt. Both of these modes offer the ability to add **Tools** and **Custom Tools**.

**What are Tools and Custom Tools for the AI Agent action?**

-   The following **Tools** can be added to the AI Agent action's configuration: [Public Templates](https://www.tines.com/docs/actions/templates/templates/), [Private Templates](https://www.tines.com/docs/actions/templates/private-templates/), and [Send to Story](https://www.tines.com/docs/stories/send-to-story/).
    
-   The **Custom Tool** option is our [action Groups](https://www.tines.com/docs/stories/groups/) feature that you can build into the AI Agent action's configuration.
    
-   A brand new action cannot be added directly as a **Tool** to an AI Agent action's configuration; it must be pre-built into one of the features mentioned, **Tools** or **Custom Tools**.
    

**What is the Think tool within the AI Agent action?**

Inspired by [Anthropic's research](https://www.anthropic.com/engineering/claude-think-tool), our custom Think tool is a scratchpad where the AI Agent action can plan its course of execution before implementation, improving its overall accuracy. The Think tool works with both built-in and custom AI providers.

**Does Tines capture an audit log of the tasks that the AI Agent action performs?**

Yes. Tenant owners can also view an AI run-time credit usage report via the Reporting tab.

**Can I expect a consistent output based on the prompt provided to the AI Agent action?**

If you are providing a prompt and expect the exact same output for each execution, we recommend using a more deterministic approach (manual building or utilizing the [Automatic mode](https://www.tines.com/docs/actions/types/event-transformation/automatic/) of the Event Transform action) vs. an agentic one.

**How many Tools can I connect to an AI Agent action?**

You can connect as many **Tools** as you need per agent. You can also use **Custom Tools**, and delegate specialized tasks within them.

**Can I run the AI Agent action in my Personal team?**

No, the AI Agent action cannot run in a personal team.

**Why does my AI agent action in task mode appear to use a different AI model when I add a tool?**

When an AI agent action is configured in task mode without any tools, it defaults to a cheaper AI model to keep costs low. Once you add at least one tool to the configuration, the agent automatically switches to the smart model to handle the increased complexity that comes with tool use. This model-switching behavior is by design — it ensures you're only using the more powerful (and more expensive) model when it's actually needed.

* * *

## Pricing

**How much does the AI Agent action cost?**

For more information on Tines' pricing, check out [our pricing and packaging article](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).

**How can I add more AI run-time credits to use the AI Agent action?**

For more information on how to add AI run-time credits to your plan, check out our [AI credits article](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits).

**How can I see the current AI run-time credit usage in my tenant?**

Run-time credits can be allocated to specific Teams in the tenant **AI settings** section to curb usage. When credit usage is nearing its limit, as well as when a team has run out of credits, an email will be sent to the tenant owner.

**Will stories using agents count towards a tenant's flow limits?**

Yes, a story utilizing the AI Agent action will count as a flow against the licensing of your Tines plan.

* * *

## Resources

**How do I get started with the AI Agent action?**

Check out our [get started article](https://explained.tines.com/en/articles/11541238-get-started-with-ai-agents) to learn more!

**Are there pre-built prompts available for the AI Agent action?**

Yes, you can find story templates using the AI Agent action in our [Library](http://www.tines.com/library/product-features/ai-agents).

**Where can I find additional resources on the AI Agent action?**

-   Tines Blog: [Introducing the AI Agent action](https://www.tines.com/blog/introducing-ai-agents/)
    
-   Tines Platform: [Agents](https://www.tines.com/platform/agents/)
    
-   Tines University: [AI Agent action](https://www.tines.com/university/builder-ai/ai-agent-action/)
    
-   [Introduction to AI Agents](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents)
    
-   [Get started with Agents](https://explained.tines.com/en/articles/11541238-get-started-with-agents)
    
-   [Best practices for the AI Agent action](https://explained.tines.com/en/articles/11644147-best-practices-for-the-ai-agent-action)
    
-   [Understanding Tines pricing and packaging](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging)

## AI usage and credits

> ***Note****: This article pertains to Tines default AI - this does not apply to those who bring their own AI model into Tines. Custom AI models do not use any AI run-time credits.*
>
> *Tines AI features run based on AI run-time credits available. All plans, including our Community Edition, receive an allocation of monthly credits by default.*

Use of AI features are subject to the terms set forth in the AI Addendum available at: [https://www.tines.com/legal/](https://www.tines.com/legal/)

### **Included credits**

All plans include a number of monthly AI run-time credits. The number of credits depends on your plan tier.

-   Community tenants are allocated 50 monthly [AI run-time credits](https://explained.tines.com/en/articles/9398967-what-tines-ai-features-are-available-to-community-users), which do not roll over to the next month. Users can top up Community Edition tenants with more credits.
    

Paid editions of Tines can purchase AI run-time credit add-ons through your Customer Success Manager or directly from the billing page (`<tenant_name>.tines.com/settings/billing`).

Credits are only deducted when the action successfully runs, i.e. it returns a message.

You can check your credit usage and allocations in your settings.

[Embedded content](https://demo.arcade.software/OVf1ByHkZP6R59llULgB?embed)

### **Top-up credits**

You can purchase additional AI run-time credits directly in-product from the billing page (`<tenant_name>.tines.com/settings/billing`) for Tines default AI models. This is available to all Tines users.

Top-up credits are only used after your monthly allocation is exhausted and will roll into the next month.

> Note: These credits do not expire. They will continue to roll over until they are exhausted.

When you top up, you'll see the dollar amount of credits used, and available. One credit equals $0.01. Hover over the dollar amount to see the credit equivalent.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2043615086/fad664f3e32bdc420798a332ec9b/CleanShot+2026-02-09+at+09_42_50%402x.png?expires=1784786400&signature=445f76a07d890f98fcd8b72204b178d79d9eb349269d525d7980e0bf6482ee93&req=diAjFc9%2FmIFXX%2FMW3nq%2BgXsIJveLKcg8TaQQNMXg3ucM1lM8MIEoAKmZDYTH%0Aoc%2Blp0QCGd152cTBPNHzq8YxaFM%3D%0A)

#### When do AI run-time credits reset in my Tines tenant?

AI credits reset on the first of the month.

> 👉 **Explore [AI in Tines](https://explained.tines.com/en/collections/9473753-ai-in-tines) to learn more about our AI offerings.**

## **AI features that use run-time credits**

There are three features in Tines that consume AI credits:

-   [AI Agent action](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents)
    
-   [Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench)
    
-   [Workbench for Storyboard](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot)
    

### **How credits are calculated**

Credits are deducted based on the actual amount charged by AWS, not a fixed amount per execution. We are charged every time data is sent to the LLM provider and returned from the LLM provider.

| AI Agent action (no tools) | Credits are deducted based on the volume of input and output data passed through the action - this is calculated each time the action runs |
| --- | --- |
| AI Agent action (tools and/or chat mode) | Credits are deducted for each conversation entry (cycle of data input, tool use, etc.) |
| Workbench | Credits are deducted for each message or tool use |

> Note: Adding tools to a Task mode agent switches it to the smart model, which may result in higher credit usage per run.

*When you use the AI Agent action type without any tools (similar to our legacy AI action)*, you'll typically use fewer credits than in the past since we only subtract credits for what is consumed rather than a flat per-execution cost. Since agents can use much larger context windows and can run multiple cycles, complex agents can consume a significant number of credits.

To understand the costs we're incurring and get a relative idea of the cost per execution, [review this AWS Bedrock documentation](https://aws.amazon.com/bedrock/pricing/) and [OpenAI's documentation](https://developers.openai.com/api/docs/pricing).

### **What if I run out of credits?**

You trust us with your most important workflows, so we protect those workflows and provide warnings when you're nearing your credit limit. You can monitor your credit usage in Reporting and track usage by team in the AI settings modal.

Tines provides notifications when your team approaches credit thresholds. It is your responsibility to monitor usage and manage consumption to avoid disruption. Credit exhaustion may result in paused AI executions until credits are restored.

For billing predictability, we do not allow your credits to exceed your purchased amount. Additional credits are available for purchase at any time.

### **Available models**

You can choose from multiple LLMs including Anthropic Claude models, OpenAI GPT models, and Amazon Nova models. We're expanding model support as AWS Bedrock and OpenAI add new options. Models will differ depending on the AWS region that your Tines instance is hosted in.

Business and enterprise customers can bring their own AI model to Tines. Learn more [here](https://www.tines.com/docs/admin/ai/).

You can check the cost of each model per credit and dollar amount in your settings. Head to your AI settings, and click `Configure` under 'Providers'. Within the pricing tab, you'll be able to see a breakdown of the cost consumption per model.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2078520022/8d4f1d68f47453b96157381dda34/AI+Cost.png?expires=1784786400&signature=fc3f2a2130e277ed5373988c2634d60904ba6fbbe617517ae2d2d1f9c5409f90&req=diAgHsx8nYFdW%2FMW3nq%2BgfDNTUv4CDYogSG0a1tI1HAs6Jcu55lNiyb3y5NA%0A27M7gfEHkD2hdaHGUIfq9jSwF08%3D%0A)

### **Rate limits**

For optimal performance, we rate limit by action runs per tenant per minute:

-   Tines AI Provider, tenant in eu-west-1: 80 runs per minute
    
-   Tines AI Provider, otherwise: 200 runs per minute
    
-   Own AI provider: 1,000 runs per minute
    

These rate limits are subject to change at Tines’ discretion to ensure system integrity.

> **Tip:** To check where your tenant is hosted, append `/info` to your tenant URL.  
> The URL should look like this: [`https://acme-corp.tines.com/info`](https://acme-corp.tines.com/info)

## ANY.RUN Authentication Guide

ANY.RUN is a malware analysis service ideal for public use by independent professionals, individual studies, and personal projects. Here's our guide for authenticating with ANY.RUN using Tines.

### First, get an ANY.RUN API key

1.  Go to [https://any.run/](https://any.run/).
    
2.  Login or sign-up.
    
3.  On the left side panel, click **Profile.**
    
    ![](https://downloads.intercomcdn.com/i/o/752130333/cef9a4dada3aab18358a0b19/image.png?expires=1784851200&signature=3be45e9f1e82ba206c39ba562b675b9a265cd242ca48dcc67e921adc8554ce36&req=cyUlF8p%2BnoJcFb4V1XW4gUkjFUBZ8HsWW9JH2JxfaW09XLr%2F6f3yuHokrlQi%0A%2Bn%2F6Qyeuq5FwwwAwEOYAZh9lvA%3D%3D%0A)
    
4.  Click **API and Limits.**
    
    ![](https://downloads.intercomcdn.com/i/o/752130565/ad998abbc40193fd7fc494a5/image.png?expires=1784851200&signature=3a9172c060c4ada5bc5e7b481df5085ccda4a60affb85d349bed43d42bbd6fad&req=cyUlF8p%2BmIdaFb4V1XW4gSB0TdmIPCzf9dkVgaXyGPYDpHwPWDZ%2BBOV8RKKI%0APLrIQNYzeHs%2FdYTM3mPh%2FecQnA%3D%3D%0A)
    
5.  Click **Generate** and copy the API key**.**
    
    ![](https://downloads.intercomcdn.com/i/o/752130997/775d7419f608c7ef61bd3740/image.png?expires=1784851200&signature=ae363ef526e2280b5e1b056dc17f54455db34848b3f38b5df00a0fceaaa27795&req=cyUlF8p%2BlIhYFb4V1XW4gWo4RR46CtN2KOi4yfwn1maObsxraJV1ZREsfUGa%0Adedk9iSB5e6YBe6YuFiJ%2B79zQA%3D%3D%0A)
    

### Then, create an ANY.RUN credential in Tines

ANY.RUN **connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "ANY.RUN" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the ANY.RUN connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the ANY.RUN credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    
    1.  **Domains:** api.any.run
        
    2.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/any-run).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of ANY.RUN stories in the [story library](https://www.tines.com/story-library?s=any.run).

## APIVoid Authentication Guide

APIVoid provides JSON APIs useful for cyber threat analysis, threat detection and

threat prevention, reducing and automating the manual work of security analysts.

### First, get an APIVoid API key

1.  Go to [https://www.apivoid.com/](https://www.apivoid.com/).
    
2.  Login or sign-up.
    
3.  Click **My API Keys.**
    
    ![](https://downloads.intercomcdn.com/i/o/752140115/2d2076c55d2faaa56304f573/image.png?expires=1784851200&signature=43cce2a514a4ae94d49222272f7e1126fe565aa6be7fe470bc5921f95155bfe1&req=cyUlF81%2BnIBaFb4V1XW4ge6dOEYlrPkLN44VoMYNOwlE9ah%2Bqbw2jblgIEP3%0AMJsPy%2BzEwAsUJB0sWJ6nuY36%2Bg%3D%3D%0A)
    
4.  Click **Add API Key** and copy the API key**.**
    
    ![](https://downloads.intercomcdn.com/i/o/752140501/436da2601607fb80a82d1e71/image.png?expires=1784851200&signature=706363f5702c3ba9f5c3214a13b8f8293776122f31d7580db7a2d817b94a2851&req=cyUlF81%2BmIFeFb4V1XW4gSv9qDN%2Fx6tLAd8SuYj%2BxgsMfWFEqcN%2Bqk0fiAZ5%0A8adrWi6i35FeoRkqx7VxwNYY5A%3D%3D%0A)
    

### Then, create an APIVoid credential in Tines

**APIVoid connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "APIVoid" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the APIVoid connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/apivoid).
>
> For more on creating credentials in Tines click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of APIVoid stories in the [story library](https://www.tines.com/story-library?s=apivoid&page=1)

## Apps in Tines

Apps let you build custom, interactive front ends for your Tines workflows in minutes, without needing a dedicated development team. Whether you want a simple intake form or a full-featured internal tool, Apps give you a polished UI that connects directly to your existing stories and automations.

## What an App is made of

Every App has three parts:

-   **UI surfaces** - the visual interface your users interact with
    
-   **A React codebase** - the underlying code that powers the App, which you can read, edit, and extend at any time
    
-   **App endpoints** - the connections between your App and your Tines stories
    

## How Apps are built

Apps are powered by **Workbench**, Tines' AI assistant. You describe what you want in plain language and Workbench generates a full React application for you. You can keep prompting to refine it, or dive into the code directly - it's your choice.

> Read more about Workbench [here](https://explained.tines.com/en/articles/9822635-introduction-to-workbench).

**The App editor gives you three main views:**

| View | What it's for |
| --- | --- |
| Preview | See the visual output of your App and interact with it in real time |
| Code | Browse files, search the codebase, and edit code directly |
| Endpoints | Connect your App to Tines data and workflows |

You're never locked into the AI-generated output. You have full control over the React code, styling, and package dependencies at all times.

## Getting started

1.  Apps live in the main navigation in Tines.
    
2.  Open the Apps section from the main navigation.
    
3.  You'll see a list of any Apps you've already created.
    
4.  Click "New" to start building a new App.  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/2470460789/16daff6727458c1d0037483073e7/CleanShot+2026-06-11+at+14_56_05.png?expires=1784786400&signature=f3aae359586f04b628b0c345ac3b97852e40cbaea5f73aaab55971b003830933&req=diQgFs14nYZXUPMW3nq%2BgcYb9c2i3DQ7pzcKQsZVC2KJCNw68uzLBgweAZbW%0A%2BuUDRrO6TxNmVpYrX%2FFjCGUhuGA%3D%0A)
    
5.  Describe what you want in the Workbench prompt - Tines will generate a React app for you.
    
6.  Use the Preview, Code, and Endpoints tabs to refine your App and connect it to your stories.  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/2470483046/7d4ea2ab32e3dc8cc167baadb1e3/apps+views.gif?expires=1784786400&signature=869c86d57a1161217e2f7f3dcd50f83e58b580f4132091eed932a794ce4a3e74&req=diQgFs12noFbX%2FMW3nq%2Bgd%2Blf2MTjk8DN7vmthtzd0LwY2DNVPpaMr5gCRzA%0A4NFc5A%2F8gWi6qPUlXJCqEJGRuVI%3D%0A)
    
7.  When you're ready, publish your App to make it available to users.
    

From there, you can explore building out more complex Apps, managing versions, and configuring admin settings to control access and distribution.

## Connecting to your workflows with endpoints

Endpoints are how your App talks to Tines. Each endpoint links your App to a story, letting you send data to a workflow or receive data back from one. A single App can connect to one story or many, so you can pull together data and automations from across your tenant into one coherent experience.

## Publishing and versioning

Apps include a built-in manual versioning system, so you decide when changes go live.

-   **Publish** an App to create a stable version that your users can access
    
-   **Revert** to a previous version if you need to roll back a change
    

This means you can iterate and experiment freely without worrying about pushing broken changes to your users.

## Aqua Security Authentication Guide

Aqua Security is the largest pure-play cloud-native security company, allowing customers to innovate and run their businesses with minimal friction. The Aqua Cloud Native Security Platform provides prevention, detection, and response automation across the entire application lifecycle to secure the build, secure cloud infrastructure, and secure running workloads wherever they are deployed.

To create an Aqua credential, you will need your Aqua:

-   `userid` - Ensure that your `userid` has sufficient privileges to access the relevant API endpoints
    
-   `password`
    

## Create an Aqua resource and credentials in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Aqua resource
    
    -   **Name:** "aqua\_userid"
        
    -   **Description:** Optional
        
    -   **Builder:** Your userid
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

You will need to create two credentials:

-   One Text type: "aqua\_password"
    
-   One HTTP request type: "aqua\_api"
    

##### Text type

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the Aqua credential
    
    1.  **Name:** "aqua\_password"
        
    2.  **Description:** Optional
        
    3.  **Value:** Your password
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the Aqua credential
    
    1.  **Name:** "aqua\_api"
        
    2.  **Description:** Optional
        
    3.  **URL:** The URL for your [region](https://support.aquasec.com/support/solutions/articles/16000127855-how-to-authenticate-to-the-enterprise-api-in-the-cspm-platform), e.g. `https://eu-1.api.cloudsploit.com/v2/signin`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste it into the "Plain code" section:  
        ​
        
        ```
        {
          "email": "<<RESOURCE.aqua_userid>>",
          "password": "<<CREDENTIAL.aqua_password>>"
        }
        ```
        
    7.  Click "Run options", navigate to the `token` key and double-click on the key
        
    8.  **Location of token from response:** Paste the path to the token in the value pill, i.e. `<<aqua_api.body.data.token>>`
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/859327797/90b1edcd16e419520e84670f/image.png?expires=1784851200&signature=f62cd6d09d73cbb3408f57c32da05b6f5ad1ed1f855fd75252eeffa5d2ffc83d&req=fCUuFct5mohYFb4V1XW4gQSzz5JMgFLEOx08pXFP4btgPhS39qjkYeUFMcNO%0ACT6kWMKWn4CHyG1UauhRMmyJ%2Bg%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/859327875/53bd1a34ac13f1703ea16db8/image.png?expires=1784851200&signature=5ae27d0f5e20df36d5062bfa252389115fc010e2070ca60e1b1419a24e9b48a9&req=fCUuFct5lYZaFb4V1XW4gUltYkj7%2FIF7yXXpEOqQN8Cra5x9TrY9mB9EmBUK%0AO3bhrWWBrqbDsVQKlCi2lxMUog%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/859327967/8fc23b27f9b03c918149ca49/image.png?expires=1784851200&signature=2a41e76970c6c8ae64f2606a9eeaf4042859b54ff5753089594d8537cb5cd38c&req=fCUuFct5lIdYFb4V1XW4gWvR%2FTdgYVroGSpnOHsCBuLVSHr1i4atx1mu%2FAdr%0Apsbvcnk0%2BdyG2wl6%2FDCTH2aZ8A%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Aqua stories in the [story library](https://www.tines.com/library?view=all&s=aqua).**

## Are rules checked in a particular order in a Trigger action?

If you have multiple rules as part of a Trigger action, they are not checked in any particular order.

By default, all rules must match for the action to emit an event. If you wish to match only a subset of the rules, for example, two of three rules, you can set that value in must\_match.

> **To learn more about trigger actions, [see here](https://www.tines.com/docs/actions/types/trigger).**

## Are there any rate limits for webhooks in Tines?

The limit rate limit for Webhooks in Tines is three thousand per minute. If you exceed this number, you may encounter error 429.

> **To learn more about webhook actions, [see here](https://www.tines.com/docs/actions/types/webhook)**.

## Armis Authentication Guide

The Armis Asset Intelligence Platform discovers and analyzes all enterprise digital assets—hardware, software, cloud or virtual--from traditional devices like laptops and smartphones to cloud workloads and new unmanaged IoT devices like smart TVs, webcams, printers, HVAC systems, Industrial Control Systems, medical devices and more. Armis discovers all asset types, provides context about them (owner, location, dependencies, vulnerabilities) and continuously analyzes their behavior to identify both operational and cyber risks to protect them against cyber attacks.

## First, get a Armis API Secret Key

1.  Login to your Armis account at `https://<tenant>.armis.com/login`
    
2.  Click "Settings" and then "API Management"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/860289163/6d500e9b0e78f9302d64b06f/image.png?expires=1784851200&signature=a1b0f1060dd06d9e1f6952d98e25c457f5853c89810be3beb7897651acd1b4a2&req=fCYnFMF3nIdcFb4V1XW4gSkWuQtaZJ6MTo%2BV58DEmN7ZoQGLkABfLrWanoME%0AKPRPYzNRhT%2BuQGpOGWe3HJNDPQ%3D%3D%0A)
    
      
    ​
    
3.  Click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/860292107/801dd0370a45a8b90172ed18/image.png?expires=1784851200&signature=181b22a66ed80901f7b3f2f18440762a75d3269580c272daa91478d2ffc8d190&req=fCYnFMB8nIFYFb4V1XW4gcc0eqHQmJ4XwgwfFDp4gnH90a%2FdR93vFyQ4u9aN%0AR7o%2Ffp%2BNy9yvMcC3ayyUo%2B3FLw%3D%3D%0A)
    
      
    ​
    
4.  Copy your "API Secret Key" to a text editor and click "OK"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/860292556/0c74e9b5d912bb5401e88349/image.png?expires=1784851200&signature=f5fc0c12b9810c1a2752e9d5cb17b725ba4c543c4cc1c252eb16f0668d34d56b&req=fCYnFMB8mIRZFb4V1XW4geTsoUB%2BwvsUEQrx86LNFXiIKcwesmzLnYq1Vpwu%0AlSq7UOhxHLjSHF9oVPi7Dqxw9A%3D%3D%0A)
    
      
    ​
    

## Lastly, create Armis credentials in Tines

**Armis connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Armis" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/armis).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Armis** **stories in the [story library](https://www.tines.com/library?s=armis&view=all)**

## Asana Authentication Guide

Asana is a project management tool for those looking for a type of software that can keep track of the important and necessary tasks related to your business that you need to accomplish in a given timeframe. It's a project management software that may help you and your team become more organized and prepared for anything.

## First, get an Asana Access Token

1.  Login to the Asana Developer Portal
    
2.  Click on the user menu and then click "Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943215200/92a13f6c4dfade56d3f374da/image.png?expires=1784851200&signature=25b0f7a6fd9b4e047bca008e642aa0bd65cba9fac5195ab180ca2f81565c1fa0&req=fSQkFMh7n4FfFb4V1XW4gajALmgik%2FSBc0GK22xrA8O8JzhV7vi9CfyvnSJi%0AX7tJFyMIVGGNaeOYCL9kV6Hipw%3D%3D%0A)
    
      
    ​
    
3.  Click on "Apps" and then click "Manage Developer Apps"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943215727/282cbae423be9dfe0c2179f5/image.png?expires=1784851200&signature=a65da3378bf9245a929049dad38afdd014af018e27af0ed3cdeb6dcec8cb3dc8&req=fSQkFMh7moNYFb4V1XW4gQ1KPd9z0pvXx9wLF%2BWvHhHyoo5EC8h61KNJYWrr%0AHCX4k7FgGJmWPQo%2BiTnuW44BOg%3D%3D%0A)
    
      
    ​
    
4.  Click "+ Create new token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943216122/36106357116fff23f81bff6c/image.png?expires=1784851200&signature=80ec6d9d9762372ce7e9919ec792e74d326032f265eb8794a02bc3cb8b9d4bb6&req=fSQkFMh4nINdFb4V1XW4gXR7GJUtXK9OlfQe2baSdvW1GGd7uj0u0iTCUm8n%0AbJe7A273XNR7dTxyIe25qeuM7g%3D%3D%0A)
    
      
    ​
    
5.  Name the token, agree to the terms and click "Create token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943218081/7df11e96a3627c127370ac71/image.png?expires=1784851200&signature=5706f799d064ccdf84cdad52b472cf4cd3d5749d8dbfcb88eef6793ada615751&req=fSQkFMh2nYleFb4V1XW4gWyn%2FIQOkM3AVSGcwHIfZm4p6d5LZZgyCsg4xxXP%0A6BLbrhaLzB5Y6kQoK5e2z9es2g%3D%3D%0A)
    
      
    ​
    
6.  Copy your token to a text editor and click "Done"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943217745/4fd3b1f365e764316a2778ca/image.png?expires=1784851200&signature=f6c7157433d85493dfd986170c99b4640f62dbeba0fb0bbaf797dd7461622559&req=fSQkFMh5moVaFb4V1XW4gYrVhUttfHsy0fA32RX5UBNjoYaLdwuhTQbW%2F9DP%0AuWBK1D84C1%2BQ8aG0KJFw%2FIWKcg%3D%3D%0A)
    

## Lastly, create an Asana credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Asana credential
    
    1.  **Name:** Asana
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Access Token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Asana** **stories in the [story library](https://www.tines.com/library?s=asana&view=all)**

## Automox Authentication Guide

Automox is a comprehensive toolkit for IT Ops teams. It's built to automate the tedious work of patching, configuring, tracking inventory, and reporting the vulnerability status of all your corporate devices – Windows, Mac, or Linux – regardless of the device's location or domain.

### First, get an Automox API key

1.  Login to your Automox account at [https://console.automox.com/](https://console.automox.com/)
    
2.  Click on the ellipsis and select "Keys"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848725956/465dfe8bd25f7d7e4e1ea5b6/image.png?expires=1784851200&signature=a0c42ec0a612b3ace55767bf738986c4581ec273f497460e90e10dc6f669e6da&req=fCQvEct7lIRZFb4V1XW4gXRHQ5bPNTfOTdaZx6WZEOCsFqCuJ8oHppNi2jVC%0AgY9d4o30VH2KZsuZ8RrSK7eHtA%3D%3D%0A)
    
      
    ​
    
3.  Click "ADD" under "API Keys"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848727203/001c9481dd2e3dd7242d1ec6/image.png?expires=1784851200&signature=4558dac748e64f7a7e64feef69ecafed74c768db6feb9df24eb329139a175a54&req=fCQvEct5n4FcFb4V1XW4gdXpNwK1vI8FXBjf2NIN1uVo8XDPkNwZMj12hydY%0AIH45rO5UC2np2dI1iVLIRtkuqw%3D%3D%0A)
    
      
    ​
    
4.  Name the API Key, set an expiration date and click "CREATE"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848729328/aba37cb29fcee4b79a35b21f/image.png?expires=1784851200&signature=872a382622e6c04aeeb1694a3b7dce2b3983a2a417540787a49e54033aa8feac&req=fCQvEct3noNXFb4V1XW4geq8yb7T0UaAGqBGpGhiQ9hkTbMZUFzlj2BYvpl7%0ATGJxePNMqXK0yfV8h0EFE8%2F2VA%3D%3D%0A)
    
      
    ​
    
5.  Copy your "API Key"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848731648/1b918a39f7e3565651073886/image.png?expires=1784851200&signature=4015f9dce21d6f33f58c13ef59a97e55d79db7f929b7a21e8d47175582df31a4&req=fCQvEcp%2Fm4VXFb4V1XW4gXCsiAAzNK4%2FTxASFG%2FSNntk9wIEmwOWKl6R1CyC%0APnXNwKkyMgYxJpBgx%2BeyT4ewAw%3D%3D%0A)
    

### Lastly, create an Automox credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Automox credential
    
    1.  **Name:** Automox
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Automox stories in the [story library](https://www.tines.com/library?s=automox&view=all)**

## Azure Authentication Guide

Azure, officially known as Microsoft Azure, is a cloud computing service created by Microsoft for building, deploying, testing, and managing applications and services through a global network of Microsoft-managed data centers. Azure provides various cloud-based services and resources to help organizations build, implement, and scale applications and infrastructure solutions. These include:

-   compute power
    
-   storage
    
-   analytics
    
-   databases
    
-   AI
    
-   machine learning
    
-   networking
    
-   security features
    

### First, create an Azure application

1.  Login to your existing [Azure](https://login.microsoftonline.com/) account or [sign up](https://signup.live.com/signup) if you haven't already signed up
    
2.  Next, you'll need to register a new Azure application in [app registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/CreateApplicationBlade/quickStartType~/null/isMSAApp~/false).  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/734622865/08fa2af071125de531536c03/Azure+2.png?expires=1784851200&signature=9487cb3d330d555a6525ef4a88078f99828d525cf22a5a746c349afecc6b581c&req=cyMjEMt8lYdaFb4V1XW4gVisv%2FWNwSOa2QAGMsIQo7dXeQs7te1Q56DPQN2k%0AUiyGlnvdDkdUFRWHmCjPq82CmA%3D%3D%0A)
    
      
    ​
    
3.  Next, in your application, navigate to Certificates & secrets > Client secrets and click on the "New client secret" button to create a client secret. Copy the secret to use later.
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799218339/46ec0af21a09f76a0b01cb86/image.png?expires=1784851200&signature=e94f3944e8454147ad06f80aef86c4b064ef33d8199c65d06366023769ceb6d1&req=cykuFMh2noJWFb4V1XW4gfJ8EvZNYzh9Oxo7%2BhMaBNWh2cCefoBmP6nRSUG%2F%0ASz%2BiIM2OJdjTK98GtZLEhWdWmA%3D%3D%0A)
    
      
    ​
    
4.  Next, navigate to API permissions and click on "Add a permission" to add the required permission depending what API you are using (see [Microsoft Azure API reference](https://learn.microsoft.com/en-us/rest/api/azure/)).  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799218805/af8679c35a950913cdfa922d/image.png?expires=1784851200&signature=766c040661d94071059f8975f446fbdae334a08d5879ff23b96d05e833c08069&req=cykuFMh2lYFaFb4V1XW4gdeuzUK2HvD1oP%2BUI5oUHINwbHslj906xNDe2L4L%0AXMsGSawNuxl3cnFGFhTp7IQn%2Bw%3D%3D%0A)
    
      
    ​
    
5.  Next, from the Azure portal, navigate to Subscriptions and click "Add" to create a subscription if you don't already have one (see [Microsoft Customer Agreement](https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/create-subscription)).  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/734624594/4d129a1dedaab15967fe0532/Azure+5.png?expires=1784851200&signature=93884c06739ad22ef62bc91d1f0c90006f7b1306c2070a6189e01c5df82f9c39&req=cyMjEMt6mIhbFb4V1XW4geNR4m%2B29%2FuOOx1a8c68P4dC7beOuG6ERcQY%2FCXZ%0A3hojHKYtsY3LA8L21fXcZtBK%2BQ%3D%3D%0A)
    
      
    ​
    
6.  Next, under your subscription, navigate to Resource groups and click "Create" to create a resource group if you don't already have one (see [Azure resource groups](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal)).  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799224708/1ab0f8373106aaa876ed36ac/image.png?expires=1784851200&signature=b192c481c5cfa7752653cbf25e5b499a90fb5ab6ed78a8c9b800a287133a7b0d&req=cykuFMt6moFXFb4V1XW4gdMyRJ9K71S8fMrEq3W7eQNooKIvXSqtZMKmBK9J%0Ap5RwgDyFNyVa%2FyjyOjWgA2%2BxnQ%3D%3D%0A)
    

> **If your application requires role-based access control (RBAC), please follow the steps below (see [What is Azure role-based access control](https://learn.microsoft.com/en-us/azure/role-based-access-control/overview))**

###   
(Optional) Create a custom role and assign it to an Azure application

1.  Under your subscription, navigate to Access Control (IAM), click "Add" and then "Add a custom role" to create a custom role  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799230326/572dcd0f21e9cff2280c9a56/image.png?expires=1784851200&signature=cb0de0e1244d8bdd6f7b12c60c39bf73a99255acd4cab042f372135a94a83a71&req=cykuFMp%2BnoNZFb4V1XW4gQ4sHbRV5SY4MEmZlAmIO9j%2BPJan4XE5MYZNZX7V%0ArZVwSmfJi9BNtQS8JrE4tVsiZg%3D%3D%0A)
    
      
    ​
    
2.  Name the role and a description, and then click "Permissions"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799231673/76ac7ce0b0c96a6903014d22/image.png?expires=1784851200&signature=cd9a02574e5fee94dae89a248e9a78c40a1bca8f01ac0362db8c7a5b6c04ea4a&req=cykuFMp%2Fm4ZcFb4V1XW4gVAinlWU4wFRyC6fPcIqJQx7W2NYHBFZ%2FTxMhh3W%0AVxLe%2BdvRII0Ebt4iADELey%2FaDg%3D%3D%0A)
    
3.  On the "Permissions" tab, click "Add permissions" to add any permissions.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799233994/a962fef190ea8ed0abaebe68/image.png?expires=1784851200&signature=611e3b9f2d215830df655c55fd80305dba596c20e33f29ea2591e3db6d84ac90&req=cykuFMp9lIhbFb4V1XW4gUsu4Rueh%2By779z7A6wlZXLOBrrktN4I27AAgTOI%0ARgMbHedcnAJeqmSmDnEuPhCF3w%3D%3D%0A)
    
      
    ​
    
4.  Alternatively, you can add permissions via the "JSON" tab. On the "JSON" tab, click "Edit" and add your permissions to the actions array. Once you have finished, click "Save".  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799232447/de1896be30cf76431fbfad96/image.png?expires=1784851200&signature=cc39a6de375a7fedda2b25a07e8d1a8a5b55dfaceb9060dac7b6bee14df441c5&req=cykuFMp8mYVYFb4V1XW4gcH9b0XqylWdGN%2B5tnBwWLu867m9skJqqvuksJjC%0AavrhAZpbxNX5uJnXNinRUv%2BubA%3D%3D%0A)
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799232527/06763ca40202b7e8dafb45ab/image.png?expires=1784851200&signature=3b38fd31d859cf367a2eccfe4c050a901dbb45441d688d1221045a5f728af416&req=cykuFMp8mINYFb4V1XW4gaS2Ox%2Fv6f%2FtdSDD4rZZYZxS%2Bqs8qdV93IFkPcFU%0ApAnLq3wYlDAbOwaQLDqEn5pj%2Bg%3D%3D%0A)
    
      
    ​
    
5.  Click on the "Permissions" tab to see the permissions.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799234736/82399f5b58a34f3a9902fd64/image.png?expires=1784851200&signature=6e702971f4cf54df2efce9ce8319c6810906272744362440085a93824832fd8e&req=cykuFMp6moJZFb4V1XW4gZFcmqcilqfwvbXFwH57urndprcizsV3lil3T3nW%0ALLYAqahXk%2F2YaDZ6NaxyOuNx4g%3D%3D%0A)
    
      
    ​
    
6.  Next, click the "Review + Create" button. If you are happy with the role, click "Create".
    
7.  Next, navigate back to Access Control (IAM), click "Add" and then "Add role assignment" to assign the role to your application.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799235923/a6982df466a676d7eec3f3e6/image.png?expires=1784851200&signature=50f6919def686728d6b6165e8e68c8681361d8d1c5b36f68e8d455e0bc271609&req=cykuFMp7lINcFb4V1XW4gS9noIZIG8%2FfwXT11nuR7iu%2BoWFZTe4pHfF4sZM7%0A4CTpYlJ5NqjSpE03BsY5xG0JUA%3D%3D%0A)
    
      
    ​
    
8.  Search for the role you created, and click on it. Select the "Members" tab and click "+ Select members".  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799236708/20610d420cc68bedb2b7b234/image.png?expires=1784851200&signature=ba448dc6b7d67409d1c0b0e7aac6bd3f4e0c2c7e3bdf94b5d2d8f08805ee551b&req=cykuFMp4moFXFb4V1XW4gewtnWFM7w99LEVmg%2B7O700bPb6eOYJZa1FS8kM8%0AynJOxQa2UHvArQ7rrFxt4byjuA%3D%3D%0A)
    
9.  Search for your application, select it, and click "Select".
    
10.  Click on the "Review + Create" button to complete the assignment.
     

  
​

### Next, create an Azure subscription Resource in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the resource
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your Microsoft subscription id  
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799239764/8707ee7614f5589a5c11c470/image.png?expires=1784851200&signature=4a642a6c01274b6eab8d41b04435f734f80409ec830bedca381df1655fb9d091&req=cykuFMp3modbFb4V1XW4gVmvs7wtPi1X%2BQAU1J74g3qrYrpmp2WkAPWuqQeR%0AhGlYgbcPY8qlc74kUDWWBzTN%2Bw%3D%3D%0A)
        
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799239975/954b54342895a306ad47600c/image.png?expires=1784851200&signature=33a4ea4e18a5575e224bdc9eca6df9bcbb56a0e5c0e28ae7044d28043f0b75d3&req=cykuFMp3lIZaFb4V1XW4gTvlNEhe9wXHO4XDQCnVa3pi1Gh0IDCVfe1PhVO8%0ApSSmBoufHmjUQqB9l3Sq55r8Gw%3D%3D%0A)
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the API
        
6.  Click "Save resource"  
    ​
    

### Lastly, create an Azure Credential in Tines

1.  Login to your Tines account.
    
2.  Select the team using the API and click "Credentials".
    
3.  Click "+ New Credential" and select "HTTP Request"***.***
    
4.  Input the values for the Azure credential
    
    1.  **Name:** Azure
        
    2.  **Description:** Optional
        
    3.  **URL**: `https://login.microsoftonline.com/<Directory tenant ID>/oauth2/token` <The Directory tenant ID can be found in your application>.  
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799241796/6813603d97a8c06d53c691f9/image.png?expires=1784851200&signature=4578d00693c1635cf6b0b7e2402bbe4ae386a4217e102a9aa494973a3ebb0be0&req=cykuFM1%2FmohZFb4V1XW4gSEdSmDFKNV8jKIPYzOP57KWpp7QnBJyIqbSoH4M%0A%2FlpYrF4td7C8klkSQ5JjhBMA6w%3D%3D%0A)
        
          
        ​
        
    4.  **Content type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:**
        
        {
        
        client\_secret: <client\_secret> created earlier
        
        client\_id: <client\_id> found in your application (see screenshot below)
        
        grant\_type: client\_credentials
        
        resource: `https://management.core.windows.net/`
        
        scope: <this should reflect your applications API permissions created earlier>
        
        }  
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799242722/4a1178190d741cc058cf9a41/image.png?expires=1784851200&signature=961330a4a9e23d53398f621ff7a19e57e3bc4e19ba999c85da6748d7709e003d&req=cykuFM18moNdFb4V1XW4gUkNJHtw1aHgPmJUQ2CRmW%2Bfwl2N%2BbY2yz3MfpBf%0AdHbj8CDWtG%2BcOFxSQ7ow9JWr7w%3D%3D%0A)
        
          
        You can paste this into the editor by clicking `<> Editor` below the **Headers** section:
        
        ```
        {
          "url": "https://login.microsoftonline.com/<directory_tenant_id>/oauth2/token",
          "content_type": "form",
          "method": "post",
          "payload": {
            "client_secret": "<client_secret>",
            "client_id": "<client_id>",
            "grant_type": "client_credentials",
            "resource": "https://management.core.windows.net/",
            "scope": "offline_access user_impersonation"
          },
          "headers": {}
        }
        ```
        
    
      
    ​
    
5.  Click "Run Options" and click the copy button besides `access_token` to copy the path.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799244312/9ac263620c428a6af8266b60/image.png?expires=1784851200&signature=66d5676d67765aaa64781f19890b87034a23a62ebc3f086a57eb997c8107c621&req=cykuFM16noBdFb4V1XW4gTIGZVQpOS6WfmbKyC8Sq4BBX4acl%2FvQqMNWpIMe%0ARpiGSTMo1rMKY01ODS7FDShnbA%3D%3D%0A)
    
      
    ​
    
6.  Next, paste the path to the `access_token` into **Location of token from response**.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799245021/1690fc06a37b7f33bc7724e4/image.png?expires=1784851200&signature=e2f36ff1576c7bc8af84f8316cab67991f9c0cd6d7cd4a6018c91a0680096347&req=cykuFM17nYNeFb4V1XW4gV3rshLnovS7GoEZobpAleBtDfPfq8N9K3ddsyTV%0AisBiz7k7fO%2BNp00zxfyPcVr1ng%3D%3D%0A)
    
      
    ​
    
7.  Click "Save".  
      
    ​
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Azure stories in the [story library](https://www.tines.com/story-library?s=azure).**

###   
Using the resource and credential in an action

  
When making requests to Azure, you can use the resource you created for your Microsoft subscription ID.  
​

```
https://management.azure.com/subscriptions/<<RESOURCE.azure_subscription_id>>/providers/Microsoft.Compute/virtualMachines?api-version=2022-11-01
```

The Header configuration for your Azure credential should be:

```
 "Authorization": "Bearer <<CREDENTIAL.azure>>"
```

Putting it all together, here is an example Azure action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"32","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"List virtual machines in Azure","description":null,"options":"{\"url\":\"https://management.azure.com/subscriptions/<<RESOURCE.azure_subscription_id>>/providers/Microsoft.Compute/virtualMachines?api-version=2022-11-01\",\"content_type\":\"application_json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.azure>>\"}}","position":{"x":780,"y":405},"schedule":[],"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:d521f765a49c72507257a2620612ee96:94658be8f4ee4b5627ed7fbcbe2fd4be"}],"links":[],"diagramNotes":[]}
```

## BambooHR Authentication Guide

BambooHR is an employee experience software that allows you to track time off, sick leave, benefits, onboarding, and more. Here's our guide on how to authenticate BambooHR for use with Tines.

## First, get a BambooHR API key:

1.  Navigate to `https://www.<<your-bamboo-tenant-name>>.bamboohr.com/home` and log into your account
    
2.  In the top right-hand corner of the screen, click on your profile and then "API Keys"
    
    ![](https://downloads.intercomcdn.com/i/o/757172745/c26bb8cf1bebeec42e1571d5/image.png?expires=1784851200&signature=40600648c22ede8d3c7d9e6039ed6eb39746b644421d25b5c12d150c29ddd187&req=cyUgF858moVaFb4V1XW4getlr0Ulj0cHshBiJJx3Ccv0XkSHuQ6pbOCb36af%0Axb2QBti4euVGgto%2FuYjv3VAdYw%3D%3D%0A)
    
3.  Click "Add New Key", name your key and click "Generate Key"
    
4.  Click "COPY KEY" to copy the key to the clipboard and click "Done"
    

## Then, create a BambooHR credential in Tines

**BambooHR connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "BambooHR" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the BambooHR connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the BambooHR credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/bamboohr).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of BambooHR stories in the [story library](https://www.tines.com/story-library?s=bamboo)

## Best practices for Automatic mode

## Overview

The [Automatic mode of the Event Transform action](https://www.tines.com/docs/actions/types/event-transformation/automatic) is powered by AI. Here, we’ll guide you through how to best guide the AI to iteratively build what you need.

## Best practices

Providing guidance to the AI is just like asking a real person for help: the clearer and simpler your request, the better the outcome is likely to be.

### Explain the input

As well as your guidance, the AI will examine the input data. But you can augment its understanding of the data. For example, you might say “the input contains a base64-encoded image”, to give the AI context on what it’s dealing with.

### **Break down into steps**

If you think the AI will need to do several things in serial to achieve your outcome, tell it. For example, you might say “First remove any blank values from the list. Then, format each value as a dollar value.”.

### **Be clear about the output you want**

For simple outputs, use plain English (“return the severity of the alert”). For more complex data structures, type out an example of the shape of the data to be returned, e.g. with JSON.

### **Check your work**

If you’re not achieving the results you expect, chances are your guidance is not clear enough. Read it as though you were the person answering the request, and tweak anything that feels potentially vague or confusing.

Most importantly, great guidance is obtained through *iteration*.

### Iterating on guidance

Instead of trying to get your guidance right in one go, it’s best to start simple and work your way to the output you need.

We’ve designed our system to author and run the underlying code very quickly, so you get the feedback you need to iterate towards success.

### **Start small**

Before you even start thinking about what output you need, consider providing generic guidance to get started, like “summarize the input”. This will give you some initial confidence that the AI is receiving and understanding the data you’re providing.

If your transformation involves several steps, perhaps start with the first step on its own

– e.g. “remove all blank values”. Or, if your transformation is a complex analysis of a list of data points, start with a simple version – e.g. “count the items in the list”.

### **Experiment safely**

The automatic mode editor is a playground: your changes do not take effect until you press “save”. Until then, any changes or errors you introduce are safely detached from your workflow.

As you iterate on your guidance, you’ll often see errors or mistakes. Each time you spot something going wrong, think about how to experiment to improve your guidance. And don’t worry: you won’t lose your work – you can always [go back and forth through your guidance history](https://www.tines.com/docs/actions/types/event-transformation/automatic).

### **Read the code (if you want)**

Automatic mode does not require you to be able to read or write code. But glancing at the code can give you hints on where the AI may be misunderstanding you.

Even if you’re not a programmer, go ahead and take a look – you might be surprised at your ability to help the AI, by looking at how it has modeled the task at hand.

### **If in doubt, rewrite**

Just like when speaking to another human, sometimes the AI won’t catch your drift. Try replacing a sentence with another way of saying the same thing, and see if you can make your intent a little clearer that way.

## Best practices for building and maintaining stories

## Overview

Building high-quality, reliable [stories](https://www.tines.com/docs/stories/) in Tines starts with following a set of consistent best practices. This guide outlines key recommendations for naming, documentation, testing, and change control. These are all designed to make your stories easier to manage, safer to run, and more effective over time.

## Best practices

### Enable change control

Enforce [change control](https://www.tines.com/docs/stories/change-control/) tenant-wide, using drafts as the "development" environment and the live version of the story as the “production” environment for each story. Require change control approvers to follow a standardized checklist of best practices when reviewing changes. Ensure that all branches of a story can be tested using the [saved story runs feature](https://www.tines.com/docs/stories/story-runs/#save-story-runs).

This mirrors a proven software development workflow, reducing the risk of breaking changes in production. It ensures every update is reviewed and tested before being deployed, creating a safer and more predictable environment for automation.

### Establish standards for story building

Enforce a standardization for the creation of stories with the following recommendations:

-   The story name is descriptive and concisely covers the usage. Whitespace and symbols are allowed (even encouraged) for readability. For example: *Proofpoint - Phishing Ticket Creation*
    
-   The story description should expand on the story's name and include details such as the creator/author(s), inputs, outputs, limitations, and expected usages. The story description should ideally cover ("It" being the story):
    
    -   "*It's designed to...*"
        
    -   "*It looks for...*"
        
    -   "*It depends on...*"
        
    -   Authors
        
-   Name each action uniquely to reflect its purpose in your story.
    
-   Enable [action/story monitoring](https://explained.tines.com/en/articles/7733319-how-can-i-monitor-actions-in-tines).
    
-   Align your actions so that they follow the path of your story (you can also utilize the Tidy feature).
    
-   Use story tags to help identify the story's usage concisely and trackably. Use tags for topics like certain use cases, frequency of use, and priority.
    
-   Utilize [note](https://www.tines.com/docs/stories/notes/) tiles throughout the story to explain the functionality for each step.
    

### Audit stories

Build a Tines story to regularly audit stories for best practices on a daily or weekly cadence. Automated audits help catch issues early and consistently, ensuring that stories continue to meet your standards over time, not just at the time of creation.

## Additional resources

-   [Optimizing Tines Stories - Tips and Tricks for Building Efficiently](https://www.tines.com/blog/optimizing-tines-building-efficiently/)
    
-   [How to Optimize Tines Stories - Tips and Tricks for Managing Stories Efficiently](https://www.tines.com/blog/how-to-optimize-tines-stories-managing-stories/)
    
-   [Tines security best practices](https://www.tines.com/tines-security-best-practices/)

## Best practices for cases

## Overview

Cases are a collaborative interface where you can investigate, remediate, and report on incidents all in real-time.

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.

## Best practices

### Case templates & data capture

Creating cases at the earliest point in your detection pipeline ensures no context is lost and reduces the time between detection and response. When an alert fires from your SIEM, EDR, or other security tools, immediately create a case with all relevant context. By using templates, you can standardize the look and feel of cases for analysts.

-   Use the Create Case action immediately after receiving alerts from your detection sources.
    
-   Design templates with consistent field structures across similar alert types (e.g., all phishing cases should have the same fields: sender email, subject line, reported by, etc.).
    
-   Pre-populate fields with data from the alert using formulas to extract and format information automatically.
    
-   Include rich context like timestamps, affected assets, alert severity, and raw event data.
    
-   Use markdown formatting in case descriptions to make information scannable with headers, bullet points, and code blocks.
    
-   Create separate templates for different incident types (phishing, malware, data exfiltration, insider threat) to ensure analysts see relevant fields for each scenario.
    

### Teams & least privilege

Implementing proper access controls protects sensitive data, prevents accidental modifications to automation logic, and ensures analysts only see cases relevant to their role.

-   Use team-based case routing to automatically assign cases to the appropriate team based on alert type or classification.
    
-   Limit story access to automation engineers and senior analysts who need to modify workflows.
    
-   Implement role-based permissions where junior analysts can view and comment, while senior analysts can close cases and execute sensitive actions.
    
-   Audit access regularly to ensure team membership reflects current organizational structure.
    

### Case actions

Case actions empower analysts to execute complex workflows with a single click, reducing response time and ensuring consistent execution of playbooks.

-   Create action buttons for common response tasks:
    
    -   "Block IP" → Add to firewall blocklist
        
    -   "Quarantine user" → Disable account and revoke sessions
        
    -   "Isolate endpoint" → Trigger EDR isolation
        
    -   "Request manager approval" → Send approval form to user's manager
        
    -   "Escalate to Tier 2" → Reassign case and notify senior analyst
        
    -   "Run full investigation" → Execute comprehensive enrichment workflow
        
-   Use deterministic actions for tasks that don't require judgment (e.g., "Gather user activity logs").
    

### Metrics and dashboard

Tracking the right metrics enables you to measure performance, identify bottlenecks, and demonstrate value to leadership. Capture important case metrics in case fields or records.

-   Store metrics in records for historical analysis and trend identification.
    
-   Create dashboard visualizations:
    
    -   Case volume by day/week/month
        
    -   Average resolution time by severity
        
    -   False positive rate by alert source
        
    -   Top alert types and their outcomes
        
    -   Analyst workload distribution
        
    -   SLA compliance rates
        

### Records

Records act as a persistent data store within Tines. Use records for capturing IOCs, correlating events, and reducing alert noise & case volume.

Learn more about records [here](https://explained.tines.com/en/articles/7971187-introduction-to-records).

-   Reduce alert noise:
    
    -   Maintain a "known good" list of benign indicators that frequently trigger false positives
        
    -   Track alert frequency per source and suppress high-volume, low-value alerts
        
    -   Implement dynamic thresholds based on historical data
        
-   Build allowlists and blocklists:
    
    -   Store approved third-party IPs/domains to filter out false positives
        
    -   Maintain internal asset inventories to enrich alerts with context
        
-   Track case outcomes:
    
    -   Record which IOCs led to true positives vs. false positives
        
    -   Use this data to tune detection rules and improve alert quality
        

### Cases webhook

Use your cases notification webhook to monitor for specific events, escalate cases, kick off workflows, and send notifications.

-   Monitor critical case events:
    
    -   Case created with high/critical severity → Immediate notification to senior analysts
        
    -   Case assigned → Send notification to assigned analyst via Slack/Teams/email
        
    -   Case updated with specific field changes → Trigger additional workflows
        
    -   Case closed → Log metrics and update dashboards
        
    -   Case reopened → Alert management to potential recurring issue
        
-   Implement escalation workflows:
    
    -   If case open > 1 hour without acknowledgment → Notify team lead
        
    -   If high-severity case open > 4 hours → Escalate to management
        
    -   If case marked as "needs help" → Assign to senior analyst and notify
        
-   Trigger downstream automation:
    
    -   When case closed as "true positive" → Add IOCs to blocklists
        
    -   When case involves specific asset → Trigger vulnerability scan
        
    -   When case indicates compromise → Initiate forensic data collection
        
-   Send contextual notifications:
    
    -   Include case summary, severity, and direct link in notifications
        
    -   Route notifications based on case team, type, or severity
        
    -   Aggregate low-priority notifications to avoid alert fatigue
        
-   Integrate with external systems:
    
    -   Create tickets in ITSM platforms when cases require IT involvement
        
    -   Update CMDB with incident information
        
    -   Log case data to SIEM for correlation with other security events
        
-   Build feedback loops:
    
    -   When analyst marks alert as false positive → Update detection rules
        
    -   When case identifies new threat → Trigger threat hunting workflows
        
    -   When case resolution involves new procedure → Update runbooks
        

## **Additional resources**

-   [Introduction to cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases)
    
-   [How to create a case in Tines](https://explained.tines.com/en/articles/7974460-how-to-create-a-case-in-tines)
    
-   [How are case IDs generated](https://explained.tines.com/en/articles/10222478-how-are-case-ids-generated)
    
-   [Additional case docs](https://www.tines.com/docs/cases/)

## Best practices for change control

## Overview

Change control helps keep your Tines tenant stable, secure, and predictable, especially as more users build and update stories. These best practices ensure that every change is reviewed, documented, and aligned with your team’s standards before going live.

## Best practices

### Enable change control

Enable [change control](https://www.tines.com/docs/stories/change-control/) for the entire tenant. Change control incorporates peer reviews before changes go live to help catch mistakes, prevent unintended impacts, and uphold shared quality standards. This safety net is especially critical in production environments where changes could affect automation outcomes.

### Manage change control requests

Use a Tines [story](https://www.tines.com/docs/stories/) to manage change control requests. Route the change control approval requests to a group of trusted Tines platform owners for review. Document change control requests in a tracking system like [records](https://www.tines.com/docs/records-cases/records/).

Automating and standardizing the change control process ensures consistency in reviews, tracking of approvals, and documentation of decisions. This provides a reliable audit trail for future reference or compliance needs.

> **Note:** If you have access to [custom roles](https://www.tines.com/docs/admin/user-administration/custom-roles/), you can fine-tune who can manage change requests. Considering a low tolerance of risk, we recommend creating a custom role with all editor permissions except for change control so the users cannot approve changes outside of your defined process.

### Establish standards

Create standardized guidelines to review against change requests and document them for all story builders and reviewers.

This checklist gives builders and reviewers a clear, consistent framework to evaluate changes against. It helps maintain high standards across the board, reduces the chance of inconsistent updates, and ensures everyone is aligned on what “excellent” looks like.

## Additional resources

-   [Optimizing Tines Stories - Tips and Tricks for Building Efficiently](https://www.tines.com/blog/optimizing-tines-building-efficiently/)
    
-   [How to Optimize Tines Stories - Tips and Tricks for Managing Stories Efficiently](https://www.tines.com/blog/how-to-optimize-tines-stories-managing-stories/)
    
-   [Tines security best practices](https://www.tines.com/tines-security-best-practices/)

## Best practices for credential management

## Overview

Managing [credentials](https://www.tines.com/docs/credentials/) securely in Tines helps protect your integrations, reduce risk, and keep your tenant organized. These best practices ensure that credentials are created, used, and maintained consistently and securely.

## Best practices

### Restrict direct access

[Restrict direct access](https://www.tines.com/docs/credentials/credential-configuration/domain-restriction/) for all credentials (tenant-level setting). Restricting direct access reduces the risk of credential exposure and misuse. This ensures credentials are only used within controlled stories, rather than being manually retrieved or shared.

### Create an approval process for credential creation

Establish a process to get approval for credential creation. Include a collection of relevant information through a [page](https://www.tines.com/docs/pages/) for a user with credential management permissions to create in the tenant. This adds a layer of oversight to ensure new credentials meet all requirements and aren’t created unnecessarily or insecurely.

### Establish standards for credential creation

Enforce a standardization for the creation of credentials with the following recommendations:

-   The credential name is filled in and includes the integration tool's name. For example: *Tines Security Automation Team API Key*
    
-   The credential description field is filled in with a relevant explanation. For example: *This Tines credential has read-only access to the Security Automation team*
    
-   The credential [product](https://www.tines.com/docs/credentials/credential-configuration/product/) field is filled in with the product you are integrating with Tines.
    
-   The credential is not a duplicate of an existing credential.
    
-   The credential is scoped to a service user or similar non-user account.
    
-   The credential [URLs and domains](https://www.tines.com/docs/credentials/credential-configuration/domain-restriction/) field for restricting domain access must not be overly permissive.
    
-   If you are using [change control](https://www.tines.com/docs/stories/change-control/), make sure the [test credential](https://www.tines.com/docs/credentials/credential-configuration/test-details-tab/) fields are complete and include info for a non-production version of the relevant tool.
    
-   If you use the [egress allowlist](https://www.tines.com/docs/admin/action-egress-control/) feature, add the relevant information to the egress allowlist to enable connecting to the tool.
    

### Audit credentials

Complete regular audits of the credentials to ensure best practices are met. Build a Tines [story](https://www.tines.com/docs/stories/) to regularly audit credentials for best practices on a daily or weekly cadence. Ongoing validation helps maintain hygiene and ensures standards are continuously met, even as new credentials are added or updated.

## Additional resources

-   [Optimizing Tines Stories - Tips and Tricks for Building Efficiently](https://www.tines.com/blog/optimizing-tines-building-efficiently/)
    
-   [How to Optimize Tines Stories - Tips and Tricks for Managing Stories Efficiently](https://www.tines.com/blog/how-to-optimize-tines-stories-managing-stories/)
    
-   [Tines security best practices](https://www.tines.com/tines-security-best-practices/)

## Best practices for managing access and permissions

### Overview

Managing who has access to what in Tines is key to keeping your tenant secure and organized. These best practices help ensure users only get the access they need, when they need it, using clear processes and visibility.

### Best practices

#### Enable SSO

Use [single sign-on (SSO)](https://www.tines.com/docs/admin/single-sign-on/) and provision users using [system for cross-domain identity management (SCIM)](https://www.tines.com/docs/admin/user-administration/scim/) or [just-in-time (JIT)](https://www.tines.com/docs/admin/user-administration/jit/) to ensure access is controlled through your identity provider (IdP).

Centralizing authentication and user provisioning through your IdP reduces manual account management, minimizes the risk of orphaned accounts, and ensures users only have access while they're authorized.

#### Utilize roles

Leverage our [default roles](https://www.tines.com/docs/admin/teams/#roles), or [custom roles](https://www.tines.com/docs/admin/user-administration/custom-roles/) (if available in your plan), to fine-tune the permissions you grant to your Tines users.

Roles allow you to precisely control what users can do within Tines. This helps reduce the risk of accidental misconfiguration or abuse of sensitive features, especially in tenants with many users or varied responsibilities.

#### Build an access request process

Set up an access request process to grant users access to teams and to assign specific roles. Include an audit trail for access requests and store it in [records](https://www.tines.com/docs/records-cases/records/) or another data storage tool. Here's an example of a story that can execute this process: [Request and approve access to new tools with an audit trail](https://www.tines.com/library/stories/1224418).

Having a structured access request and approval process enforces the principle of least privilege and ensures accountability. Storing audit trails in Tines records or another storage tool allows teams to track who requested access, who approved it, and when. This is critical for audits, compliance, and incident investigations.

> **Note:** If you utilize a [tunnel](https://www.tines.com/docs/admin/tunnel/) or [command-over-HTTP](https://www.tines.com/docs/admin/command-over-http/), provide access to the tunnel via a specific team or teams on an as-needed basis.

## Additional resources

-   [Optimizing Tines Stories - Tips and Tricks for Building Efficiently](https://www.tines.com/blog/optimizing-tines-building-efficiently/)
    
-   [How to Optimize Tines Stories - Tips and Tricks for Managing Stories Efficiently](https://www.tines.com/blog/how-to-optimize-tines-stories-managing-stories/)
    
-   [Tines security best practices](https://www.tines.com/tines-security-best-practices/)

## Best practices for security and monitoring

## Overview

Strong security and effective monitoring are critical for maintaining trust, minimizing risk, and ensuring reliable operations in Tines. This guide outlines key best practices for audit logging and alerting, helping teams stay proactive and secure in their automation flows.

## Best practices

### Utilize SIEM

If applicable, forward all of your [audit logs](https://www.tines.com/api/audit-logs/) to your SIEM tool. We recommend using your SIEM tool to generate appropriate alerts and pipe alerts back to Tines to take action on.

Sending audit logs to your SIEM centralizes visibility and allows your security team to detect and respond to activity in real time. It also supports compliance requirements and enables historical investigation when needed.

> **Note:** Need some inspiration? Here are a few templates from our story library to help you get started!
>
> -   [Ingest Tines audit logs into Datadog](https://www.tines.com/library/stories/1251311/?name=ingest-tines-audit-logs-into-datadog)
>     
> -   [Ingest Tines audit logs into Snowflake](https://www.tines.com/library/stories/1286491/?name=ingest-tines-audit-logs-into-snowflake)
>     
> -   [Ingest Tines audit logs in Splunk](https://www.tines.com/library/stories/1111551/?name=ingest-tines-audit-logs-into-splunk)

### Set up monitoring alerts

Send [action/story monitoring alerts](https://explained.tines.com/en/articles/7733319-how-can-i-monitor-actions-in-tines) to a Tines story that alerts builders on failures in their stories. Monitoring alerts help teams quickly identify and fix issues in their stories. Routing those alerts into a Tines story keeps the response automated and ensures failures don’t go unnoticed.

### Monitor user activity

We recommend building a [story](https://www.tines.com/docs/stories/) to monitor audit logs for unusual activity. This adds an extra layer of automated threat detection, especially useful for large teams or high-security environments. It helps spot potential misuse early, even if it slips past manual review.

## Additional resources

-   [Optimizing Tines Stories - Tips and Tricks for Building Efficiently](https://www.tines.com/blog/optimizing-tines-building-efficiently/)
    
-   [How to Optimize Tines Stories - Tips and Tricks for Managing Stories Efficiently](https://www.tines.com/blog/how-to-optimize-tines-stories-managing-stories/)
    
-   [Tines security best practices](https://www.tines.com/tines-security-best-practices/)

## Best practices for the AI Agent action

## Overview

The AI Agent action is a powerful tool in Tines that uses AI language models to perform automated tasks or engage with users in real time. This article outlines key recommendations to help you design the AI Agent action effectively.

## Builder best practices

### Precision prompting

Within the AI Agent action, there are a few options where you can input guidance:

-   The **System instructions** set the stage for the AI Agent action's behavior. Define the tone, domain, persona, and rules it should follow here.
    
-   The **Prompt** is for passing the list of instructions the AI Agent action will carry out. Provide the steps the AI Agent action should execute here, including any upstream reference data.
    

In general, providing guidance that is clear, grammatically correct, and detailed will ensure a successful output. You also don't need to greet or thank the AI Agent action, as handling the additional logic for this can cost more AI run-time credits. Learn more about how to write effective prompts [here](https://explained.tines.com/en/articles/12097316-writing-effective-prompts).

> **Note**: The AI Agent action uses AI run-time credits. You can see the real-time credits used per team in the **AI settings** section of the tenant **Settings** menu. Learn more about AI run-time credits [here](https://explained.tines.com/en/articles/9369092-ai-action-credits-executions).

Here are some simple examples of how you can utilize both the **System instructions** and **Prompt** options of the AI Agent action:

#### Task mode example

-   **System instructions:** *You are an internal support automation agent for a SaaS integration company. Always use a neutral, professional tone. Only include factual information and omit small talk or irrelevant messages.*
    
-   **Prompt**: *Summarize the conversation provided in `message.body` in 2–3 sentences. Focus on the issue raised, any troubleshooting steps taken, and the final outcome. Then, post the conversation summary in the Slack channel provided via `slack.channel.id`.*
    

#### Chat mode example

-   **System instructions:** *You are a helpful onboarding assistant for new employees at a SaaS integration company. Use a friendly, concise tone.*
    
-   **Prompt**: *Respond to new hire questions about tools, processes, or policies. Provide clear answers, link to relevant resources if available (provided via `resources.list`), and encourage them to reach out to HR for anything sensitive. If a topic still requires human follow-up, provide the user's manager's contact info via `user.manager`.*
    

If you're unsure of what to include in your system instructions and prompt, you can use **[Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench)** to help you with this. Just describe the outcome you're looking for, and ask it to create these instructions for you. You can then test and edit these as needed.

> Tip: Understand how tools affect model selection.
>
> In Task mode, the AI Agent action defaults to a cheaper AI model when no tools are configured. Once you add a tool, it automatically switches to the smart model. This is expected behavior, keep it in mind when designing your agents:
>
> -   If your task only requires text processing or summarization with no external actions, consider leaving tools out to benefit from the lower-cost model.
>     
> -   If your task requires tool use, the smart model will be used automatically, which may increase credit consumption.
>     
>
> This is another reason to follow the "avoid the do everything agent" principle — specialized agents with only the tools they need will be more cost-efficient.

### Use XML tags

Using XML tags in your system instructions and prompts can help organize complex instructions and improve the AI Agent action's ability to parse different sections of your guidance. XML tags act as clear semantic boundaries that separate instructions, context, examples, and data.

They can be used for adding structure to instructions, defining examples and separating data from instructions.

You can use any descriptive tag names that make sense for your use case. Common examples include `<instructions>`, `<context>`, `<examples>`, `<data>`, and `<constraints>`. Keep your tag names consistent across similar AI Agent actions to maintain clarity across your workflows.

**XML tag example**

Build an AI Agent action that analyzes security incidents. Use XML tags to separate the task instructions from the incident data:

**Example prompt**

```
<task>
Analyze the security incident below and determine:
1. Severity level (Low, Medium, High, Critical)
2. Affected systems
3. Recommended immediate actions
</task>

<incident_data>
{{.webhook.body.incident_details}}
</incident_data>

<output_format>
Provide your analysis in a structured format with clear headings for each section.
</output_format>
```

### Avoid the "Do everything" agent

Having a single AI Agent attempt to perform multiple complex tasks simultaneously can lead to several issues:

-   **Context overload:** The agent may run out of context window space due to data sprawl
    
-   **Task interference:** Multiple objectives can create conflicting priorities
    
-   **Reduced accuracy:** Performance degrades as complexity increases
    
-   **Inefficient resource usage:** Consumes more AI run-time credits than necessary
    

Instead, consider breaking down complex tasks into their constituent parts and creating specialized agents for each component. This approach allows each agent to excel at a specific task while maintaining a cohesive workflow.

For example:

*"Retrieve security alerts from the past 24 hours, identify potential threat patterns, correlate with known IOCs, evaluate affected asset criticality, and output prioritized remediation recommendations."*

Rather than having one agent attempt this entire process, you could implement a workflow with four specialized agents:

1.  **Alert Collection Agent:** Focuses solely on retrieving and normalizing security alerts
    
2.  **Pattern Analysis Agent:** Identifies potential threat patterns and correlations
    
3.  **IOC Enrichment Agent:** Correlates findings with threat intelligence feeds
    
4.  **Prioritization Agent:** Evaluates asset criticality and outputs prioritized recommendations
    

Each agent would share the same foundational system instructions but have a more defined goal and task prompt. By connecting these agents in a semi-deterministic flow, you create a complete and more effective security investigation process.

### Utilize the Description section

As you integrate the **Tools** of the AI Agent action, use the **Description** section of each Tool to provide specific instructions for the AI Agent action to follow. The Description is passed to the AI model and influences how and when it uses the Tool.

#### Description example

Build a story that uses the AI Agent action to search potentially harmful IPs against AbuseIPDB and VirusTotal. It then creates a Tines case with the results. To help the AI Agent action function more proficiently, provide similar instructions to the Tines "Create a case" Tool's Description:

*"Only create a case after all provided IP addresses have been searched. The case name value must include today's date in a DD/MM/YYYY format."*

![](https://downloads.intercomcdn.com/i/o/am0g137l/1586041482/ed8e2ea9ef518bfdb0ff62d03c20/image.png?expires=1784786400&signature=cf48ef98c102b3320a6df6127754f61fd9b4f9199c947d0dab8a6037315e7af9&req=dSUvEMl6nIVXW%2FMW3nq%2BgQPPOTLs%2F0xQj0cIoN2VJHX6x9PObQbqSzX84GzZ%0AHU3mPasU86TQY1ZQLsY9X3PpB%2Bk%3D%0A)

### Combine similar action templates into Custom Tools

If you find yourself running the same set of **Tools** within the AI Agent action, consider combining them into a **Custom Tool** or building the action templates and Tools within a **[Send to Story](https://www.tines.com/docs/stories/send-to-story/)** and connecting it within the AI Agent action's configuration**.** This is a great way to boost it's execution speed and reduce AI run-time credit costs.

> **Note**: Although **Custom Tools** and **[action Groups](https://www.tines.com/docs/stories/groups/)** are similar in configuration, **Custom Tools** cannot be ungrouped.

### Specify end results with Output Schema

Using the **Output Schema** feature via the `+ Option` button of the AI Agent action helps enforce consistency and ensures the output follows a specific format, making it easier to parse and use the event data programmatically. You can utilize the **Prompt** and **System instructions** by referencing the **Output Schema** to organize how the result data should be structured.

> **Tip**: Check out the [JSON Schema website](https://json-schema.org/learn/miscellaneous-examples#basic) to learn more about successfully building a structure for the **Output Schema**.

#### Output Schema example

Build an AI Agent action in **Task** mode that acts as a user investigation and enrichment agent. Define the **Output Schema** and clarify the requirements for using it throughout the **System instructions** and **Prompt**:

**Output Schema**

```
{
  "title": "Person",
  "type": "object",
  "properties": {
    "fullName": {
      "type": "string",
      "description": "The person's full name."
    },
    "age": {
      "type": "integer",
      "description": "Age in years which must be equal to or greater than zero."
    },
    "email": {
      "type": "string",
      "description": "The person's email address."
    }
  },
  "required": [
    "fullName",
    "email"
  ]
}
```

**System instructions**

```
You are a User Investigation & Enrichment Agent that gathers information about individuals from public sources to create comprehensive profiles while maintaining strict privacy standards. You are running autonomously.

Core Responsibilities: 
- Investigate individuals based on minimal identifiers (name, email, etc.)
- Collect only publicly available information from legitimate sources and tools
- Verify data across multiple sources when possible - Format results according to the required JSON schema.  

Investigation Process: 
1. Begin with provided identifier 
2. Search public professional sources (LinkedIn, company websites) 
3. Cross-reference information for verification 
4. Document sources for all collected data 
5. Format according to schema, omitting uncertain fields
```

**Prompt**

```
Investigate and create a profile for this individual: <<user_data>>
Using the various tools available, compile:
- Full name
- Approximate age
- Hobbies/interests
- Professional contact information
Format according to the required schema. Omit any fields that cannot be verified with reasonable confidence.
```

### Set up monitoring

We recommend setting up failure **Monitoring** for all actions across your stories. You can set up Monitoring and contact options via the story's editor panel. Or, you can set up Monitoring per-action via the action's **Status** tab:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1592006477/7b9d6a5759c37dbc84fb4001a242/image.png?expires=1784786400&signature=81eb80f25dfe9f3a19a9b89f71853f212a5e97b78c42d3c9aaa23cba76b2c219&req=dSUuFMl%2Bm4VYXvMW3nq%2BgRfRHy8%2FJmlkbKJIInrd750R8XtGxb%2BbnX61U4Cx%0ArUqjVzpZBfNZkSjqQUzvmbavp1c%3D%0A)

## Admin best practices

### Manage AI run-time credit allocations

Regularly adjusting your Teams' AI run-time credit allocations allows you to stay aligned with shifting needs while maintaining operational efficiency. You can delegate your AI run-time credits across your Teams via your tenant's **Settings** menu → **AI settings** → **Providers** → **Configure** → **Allocation**:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1592106420/9eecb354ca8a063488d38e05ac58/image.png?expires=1784786400&signature=3349f9b552f9744dc118dcb94d7efa3badae2ca852b76bf83ca71db25e5672f7&req=dSUuFMh%2Bm4VdWfMW3nq%2BgXt1izi5dSmyM49g3mukf1Lllo3ofatqCg5762Br%0ALtYFGoPaxjGsHMTHYc6InG23s5k%3D%0A)

### Allocate emergency savings

To maintain stability and prevent disruptions in your workflows, we recommend reserving ~10% of your total monthly AI run-time credits as a buffer. This emergency reserve acts as a safeguard in case a Team unexpectedly needs to exceed their allocated AI run-time credits due to high-priority investigations or surges in user activity.

### Keep track of AI run-time credit usage

You can view your tenant's overall AI run-time credit usage by visiting the **Reporting** page → **AI credit usage**:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1591992373/57c253dd45a845b966b204921ef6/image.png?expires=1784786400&signature=0402a00dc6e9a7022fef5857e6f353d146d6ad4eb015a17e9b11ee9aa942508c&req=dSUuF8B3n4JYWvMW3nq%2BgY%2FTpO8RxJm0c1r7BeUMx1QTDYw7q6WVjaQXjKuH%0ALwn4VKcM9VH%2BCnAhfOi3m9mHkRk%3D%0A)

The **Meta** output of each AI Agent action run will include the credit cost to run and the remaining credits available to the Team in which the story resides. Encourage your builders to create additional logic that monitors for intense AI Agent actions that execute exceptionally high credit usage runs:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1591504292/091b4056e041ec43632847369a7e/image.png?expires=1784786400&signature=b188f5879d1a420715a5204dfec7b7eb5e70e07bfc0b69afe1a89498833f3d9f&req=dSUuF8x%2BmYNWW%2FMW3nq%2BgWF%2BAiAHqYAcPMSDckkn%2BRYiAPBXd8snM3PXM8RT%0AaqRfli8E3k%2FN4utl5Ud8oJaStF4%3D%0A)

## Additional resources

-   Tines Blog: [Introducing the AI Agent action](https://www.tines.com/blog/introducing-ai-agents/)
    
-   Tines Platform: [Agents](https://www.tines.com/platform/agents/)
    
-   Tines University: [AI Agent action](https://www.tines.com/university/tines-basics/using-ai-in-tines/agents/)
    
-   [Introduction to AI Agents](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents)
    
-   [Get started with Agents](https://explained.tines.com/en/articles/11541238-get-started-with-agents)
    
-   [AI Agent action FAQs](https://explained.tines.com/en/articles/11510164-ai-agent-action-faqs)
    
-   [Understanding Tines pricing and packaging](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging)

## Best practices for the Tines tunnel

## Overview

To get the most out of your Tines tunnel while keeping security tight, it's important to manage how it’s updated, monitored, and controlled. These best practices will help you maintain tunnel performance, reduce risk, and improve visibility into how data flows through your environment.

## Best practices

### Stay updated

Upgrade the tunnel whenever we release a new version. Tunnel updates often include security patches, performance improvements, and new features. Staying up to date reduces vulnerabilities and ensures compatibility with the evolving Tines infrastructure.

### Limit egress

Apply network settings to the tunnel to limit egress to internal IP ranges and Cloudflare. Restricting the tunnel’s egress helps prevent unauthorized or accidental data exposure by ensuring traffic only reaches known, trusted destinations. It minimizes your attack surface and aligns with the principle of least privilege.

### Packet analysis

Set up networking tools to complete packet analysis on the tunnel. Packet analysis provides deeper visibility into tunnel traffic, helping detect anomalies or misuse. It’s especially valuable for debugging connectivity issues and ensuring data is flowing securely and as expected.

## Additional resources

-   [Tines security best practices](https://www.tines.com/tines-security-best-practices)

## Britive Authentication Guide

Britive is a cloud-native security solution built for the most demanding cloud-forward enterprises. Their platform empowers teams across cloud infrastructure, DevOps, and security functions with dynamic and intelligent privileged access administration solutions for multi-cloud environments.

## First, get a Britive API token

1.  Login to your Britive account
    
2.  From the "Admin" menu, click "Security"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943219256/119b4b119fe0076b6538265a/image.png?expires=1784851200&signature=15f3bc5b67de7cfb3e42a4fffdf08f271636074817a9f621eeb77ba33b881f0f&req=fSQkFMh3n4RZFb4V1XW4gQ0PlvXjFhzsZS3rVvj6vQqBUI7Vm1l%2F8NbL46F4%0A9UpyAHofEGwo8YZGXHRkYOGRbw%3D%3D%0A)
    
3.  Click "API Tokens" and then click "Create token"
    
4.  Name the token and click "Create token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943219625/d417419a03bd32f4632b8b60/image.png?expires=1784851200&signature=c3207ae0f9da845ded273c17861542c6ab8a416a22eea2c6e966d7a30fbb9557&req=fSQkFMh3m4NaFb4V1XW4geErZLeZE%2FeuhzmIAOloL24oUxCeIki4rpSuZ9bG%0AB1NBRt49z2cb%2FFlSjmR9eNxtUg%3D%3D%0A)
    
      
    ​
    
5.  Copy your API token to a text editor
    

## Lastly, create a Britive credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Britive credential
    
    1.  **Name:** Britive
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Britive** **stories in the [story library](https://www.tines.com/library?s=britive&view=all)**

## Building Apps in Tines

Apps give you a dedicated surface to build interactive, user-facing experiences that connect directly to your Tines workflows. Whether you're collecting input, displaying data, or triggering automations, the App builder takes you from idea to working app in minutes.

## Getting started

Navigate to the Apps section in Tines. The first time you visit, a short intro will walk you through the basics. When you're ready, click "New" to create your first App.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2470658568/4d7dc475eba27f24a8ff9790d155/apps+new.gif?expires=1784786400&signature=9842d3b28d89c074ce92faad12ccb1e7112d4bd3f1e70e9a0784d7e65cce0ef5&req=diQgFs97lYRZUfMW3nq%2BgURMZZzV3K2r7FIafvAhUM77GXqP%2FpWXsa7rW1Fm%0AU3A%2BjOfF2y8OJwiA9ofHjM1j0CI%3D%0A)

## The App builder

Once your App is created, the build process happens in the left-hand conversation panel. Type a message describing what you want to build and Tines will generate an initial version of your App within minutes.

> Note: Write clear prompts to get better results.

Here's what to include:

-   What you want the App to do, including specific features or functionality
    
-   Whether the App should be powered by real data and connect to an existing story
    
-   The intended look and feel. You can upload mockups, references, or describe the design direction
    
-   Whether you want endpoints generated automatically (just say so in your prompt and it's handled for you)
    

You can keep refining your App through follow-up messages in the same conversation.

### Previewing your App

The preview tab shows the visual output of your generated App. It's a fully interactive sandbox so you can click through your App exactly as a user would.

Use the built-in browser controls to:

-   Refresh the view
    
-   Navigate back and forward
    
-   Toggle between desktop and mobile viewport sizes
    

### Editing code directly

The code tab contains all of the React code generated for your App. From here you can:

-   Browse files and search within the codebase
    
-   Edit code directly in the editor
    

To save your changes:

-   Single file: Cmd + S / Ctrl + S
    
-   All files at once: Cmd + Option + S / Ctrl + Alt + S
    

### Connecting your App to workflows with Endpoints

Endpoints are what link your App to Tines workflows. They let your App send data to a story or receive data back from one.

There are two ways to create endpoints:

#### Workbench-generated endpoints (recommended)

Workbench acts as a built-in companion that can create endpoints for you automatically. From the conversation panel, ask Workbench to create or connect endpoints, and it will either generate them within an existing story or spin up a brand-new dedicated story for your App, all without leaving the builder.

#### Manually created endpoints

If you've already built a workflow you want to expose in your App, you can manually define endpoints in an existing story or a new one. Enable the story's inputs and outputs as App endpoints so your App can interact directly with that story and its data.

## Building blocks - Bulk API Requests

## Overview

While working with API platforms, you may need to run multiple values against a single API endpoint. In Tines, you can accomplish this using the explode mode of our event transform action or with our group looping functionality.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Explode mode](https://www.tines.com/docs/actions/types/event-transformation/explode/)
    
-   [Tines - Group loops](https://www.tines.com/docs/stories/groups/#looping)
    

## Make it happen

**Scenario:** We'll be utilizing the open API, [PokéAPI](https://pokeapi.co/), and its following endpoint: `https://pokeapi.co/api/v2/pokemon`.

-   First, we want to pull a list of Pokémon using the endpoint that returns an array of names. By default, this endpoint returns 20 results.
    
-   Then, we want to pull the details of *each* Pokémon by passing their name in the next request: `https://pokeapi.co/api/v2/pokemon/{name}`.
    
-   Lastly, we want to see all of the details pulled for each Pokémon in a single output.
    

### Bulk API requests with explode

In this video, we look at how to approach the "Make it happen" scenario using explodes and implodes.

[Watch video](https://www.youtube.com/embed/IsuVYaYCJ0s?rel=0)

**Written steps:**

1.  Create an `GET` HTTP request action called "Get Pokemon Names" that reaches out to the `https://pokeapi.co/api/v2/pokemon` endpoint.
    
2.  Create and connect an event transform action called "Explode Pokemon" set to explode the `body.results` array of the "Get Pokemon Names" action's output.
    
3.  Create and connect another `GET` HTTP request action called "Get Each Pokemon Detail" that reaches out to the endpoint, but with each exploded name appended to the URL in a Value pill from the "Explode Pokemon" action: `https://pokeapi.co/api/v2/pokemon/<<explode_pokemon.individual_pokemon.name>>`.
    
4.  Create and connect an event transform action called "Implode Pokemon" set to implode the results of every "Get Each Pokemon Detail" event into one array.
    

### Bulk API requests with group loop

In this video, we look at how to approach the "Make it happen" scenario using group looping.

[Watch video](https://www.youtube.com/embed/kTKJrolIqnI?rel=0)

**Written steps:**

1.  Create an HTTP request action called "Get Pokemon Names" that reaches out to the `https://pokeapi.co/api/v2/pokemon` endpoint.
    
2.  Create and connect a group called "Get Each Pokemon Detail".
    
3.  For the "Get Pokemon Details" group, click + Option -> add the `Loop` option.
    
4.  In the `Loop` option, set the following in a Value pill: `<<MAP(get_pokemon_names.body.results, "name")>>`. This uses our `MAP` [function](https://www.tines.com/docs/formulas/functions/map/) to create an array of Pokémon names that the group loop will iterate through.
    
5.  Click inside the group. You will see Input and Output tiles.
    
6.  Within the group, create and connect an HTTP request action between these two tiles called "Get Each Pokemon Detail" that reaches out to the endpoint, but with each name from the group loop input set in step 4 appended to the URL in a Value pill: `https://pokeapi.co/api/v2/pokemon/<<input.payload>>`.
    
7.  Configure the Output tile's payload to the results of the "Get Each Pokemon Detail" HTTP action in a Value pill: `<<get_pokemon_details.body>>`.
    

### Which method should I use?

Both methods have their use cases:

-   **Explode → Implode**: allows you to see all the events at the root of the story. Exploded events are processed in parallel, not sequentially. This improves performance, but also means that the order of the events that went into the explode may not always be in the same order out of the implode.
    
-   **Group Loop:** simplifies the visual look of this flow, reducing the visible event count and action tiles at the root of the story. By default, group loops are processed sequentially. However, we offer the `Run loop in parallel` option for group loops, which acts the same as the explode-implode behavior.
    

Take a moment to compare their visual results within the root of the story:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1374277298/565a1afbff0ecd6214da99ca7d03/image.png?expires=1784851200&signature=796eca429d2e7ac563a8142f5d0577ca569730decfeb2ee8f384b2c3b6983883&req=dSMgEst5moNWUfMW3nq%2BgSI5OAqexRQSVveq3keOnR7fnajoqUhWnSuV3cPo%0AQO5ZG6cOJ9uC1cEy4YCzKhH3iYk%3D%0A)

## Building blocks - Event data flow

## Overview

In this article we'll go over the ways you can reference action event data as it flows through your story. Specifically, we'll take a look at how you can map this output data from one action into subsequent actions. You'll hear this commonly referred to as "upstream events" or "upstream event data."

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Events](https://www.tines.com/docs/events/)
    
-   [Tines - Functions](https://www.tines.com/docs/formulas/functions/)
    
-   [Tines - Referencing data](https://www.tines.com/docs/formulas/referencing-data/)
    

## Make it happen

We'll use the following sample of a "Get User Info" action's event data that kicks off a story:

```
{
  "user": "Jane Tino",
  "email": "jtino@tines.io",
  "usernames": [
    {
      "value": "jtino",
      "is_active": true
    },
    {
      "value": "jane_tino",
      "is_active": false
    }
  ]
}
```

### Basic

Throughout these options, let's grab the user's email from the"Get User Info" action's event data. When referencing upstream event data, the JSON path will always start with the referenced action's name in [snake case](https://en.wikipedia.org/wiki/Snake_case) format (i.e. Get User Info → `get_user_info`).

#### ⚙️ Option One: Type it out

[Watch video](https://www.youtube.com/embed/1j4Zcphkdas?rel=0)

1.  Within your subsequent action, navigate to where you want to reference the upstream data. In our example, this will be within the Payload.
    
2.  Click + → Value (shortcut: `cmd + space` | `ctrl + space`)
    
3.  Write out the JSON path with your keyboard: `get_user_info.body.email`
    
    -   As you type out each segment, Tines will try to suggest the next part of the JSON path.
        
4.  Reference the "Results" on the bottom-right to confirm the data element you are targeting. In our example, this will be: `"jtino@tines.io"`.
    

#### ⚙️ Option Two: Click through the path

[Watch video](https://www.youtube.com/embed/eOGXSWfQsDM?rel=0)

1.  Within your subsequent action, navigate to where you want to reference the upstream data. In our example, this will be within the Payload.
    
2.  Click + → Value (shortcut: `cmd + space` | `ctrl + space`)
    
3.  Click on the action name you want to get the data path for in the formula widget. For this example, this would be: `get_user_info`
    
4.  Each time you click on the element it will auto-fill. This is similar to option one via typing, however, this method tends to be quicker: `get_user_info.body.email`
    
    1.  **Note:** You will need to ensure that a `.` is between each value. You can do so by clicking twice between each.
        
5.  Reference the "Results" on the bottom-right to confirm the data element you are targeting. In our example, this will be: `"jtino@tines.io"`.
    

> **Tines Tip**: Instead of clicking, you can also go through the JSON path by pressing `enter`/`return`!

#### ⚙️ Option Three: From the Events panel

[Watch video](https://www.youtube.com/embed/FiieLHRvREM?rel=0)

1.  Locate the upstream event data that you want to map the data from.
    
2.  Go upstream to the desired action.
    
3.  Open the Events panel of that action.
    
4.  Navigate to the part of the data you want by clicking the "..."
    
5.  While hovering over the object, click on the copy icon that appears on the left-hand side. This will copy the specific JSON path.
    
6.  Navigate to your subsequent action + where you want to reference the upstream data. In our example, this will be within the Payload.
    
7.  Execute a paste (`cmd + v` | `ctrl +v`).
    
8.  See your Value pill with the copied JSON path configured.
    

> **Tines Tip:** When referencing JSON paths from upstream actions, you will need event data to be able to review the output preview in the "Results" on the bottom right of the formula widget. Without it, the "Results" view will show as empty, or sometimes `null`.

### Advanced

#### ⚡️ Arrays - Static referencing

[Watch video](https://www.youtube.com/embed/ffWkDyE6rtA?rel=0)

Take a look at the `usernames` array in our "Get User Info" action. Let's say we want to capture the `jane_tino` username. We can see that `jane_tino` is part of the 2nd object in the array. So, to reference this in our subsequent action, we'll capture the following JSON path via one of the basic options above: `get_user_info.body.usernames[1].value`.

> **Tines Tip**: The reason why the 2nd object in the array is referenced as index 1 in the path is because array indexes start at 0.

However, what happens if that order is swapped, or, we only want to pull active usernames?

#### 🌊 Arrays - Dynamic referencing

If we want to be able to capture a specific set of data, we can integrate [Tines functions](https://www.tines.com/docs/formulas/functions/) into our setup. Functions let us use programmatic logic to capture data in a more dynamic way. Let's see how we could filter the array and keep usernames where `is_active` is `true`, using our `WHERE` [function](https://www.tines.com/docs/formulas/functions/where/).

[Watch video](https://www.youtube.com/embed/w_oX8XAzvQE?rel=0)

1.  Within your subsequent action, navigate to where you want to reference the upstream data. In our example, this will be within the Payload.
    
2.  Click + → Value (shortcut: `cmd + space` | `ctrl + space`) (make sure to change to Formula mode!).
    
3.  Begin typing out `WHERE` in the formula widget.
    
    1.  As you start typing this out, Tines will suggest functions that match the letters provided.
        
4.  Select `WHERE`.
    
    1.  Our `WHERE` function accepts the following syntax: `WHERE(array, path, [value])`. Essentially, `WHERE` looks at an array and targets a key path using a comparable value.
        
5.  Since we want to only to see active usernames, format the `WHERE` function as follows: `WHERE(get_user_info.body.usernames, "is_active", TRUE)`. (**Note**: `TRUE` is highlighted + formatted in all-caps due to it being a [boolean](https://en.wikipedia.org/wiki/Boolean_data_type).)
    
    1.  Make sure to change the data type to **Formula** mode so `WHERE` formats the array correctly!
        
6.  Reference the "Results" on the bottom-right to confirm that the final output will only include usernames where `is_active` is set to `true`.

## Building blocks - HTTP request action

## Overview

HTTP request actions come into play when you need to reach out to other platforms in your story. In this article, we'll go through a checklist, including some tips of building out an HTTP request action.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Action templates](https://www.tines.com/docs/actions/templates/templates/)
    
-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Shortcuts: Import from cURL](https://www.tines.com/docs/actions/shortcuts/#import-from-curl)
    

> **Tines Tip**: Need some inspiration with building out your story? Check out our [story library](https://www.tines.com/library/)!

## Make it happen

### ✅ Check one: Is there a Tines template?

It may be possible that a [template](https://www.tines.com/docs/actions/templates/templates/) has already been created for the API call you're looking to make.

1.  Grab from the templates on the bottom left and drag into the story.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1273875705/91e4ca09c7c3bb36dd9582e8c36f/image.png?expires=1784851200&signature=a808f77dafbadbfae72de6bb99857b5cfe39b85e16021636388c345118bf935e&req=dSIgFcF5mIZfXPMW3nq%2BgfVmuKFMbWNdYSwaXQQZZUW4L%2BzpU9Rk0ET8mVSE%0AWM9M6yIXg7%2B8mNfoZm4uHayDO%2BU%3D%0A)
    
2.  Then select the task from the template you'd like to use.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300145480/3b31a964f712db99b75d14d5f2e7/image.png?expires=1784851200&signature=26dd8aa10a17f28d598693554f61ac4cf3a2bd3f41b49876c5c3e889c20e6fa0&req=dSMnFsh6mIVXWfMW3nq%2BgbfUaVLISpRRzXdXHiu1N3cWqOGkKc0rBAjp%2F853%0AJwFvg136twPR3MGcY33SOJ1idp0%3D%0A)
    

### ✅ Check two: Does the platform offer API documentation?

Visit the platform's API documentation to explore the types of calls can be made. Let's look at an example using Slack:

1.  Using your search engine, enter the platform's name and API documentation.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1249422999/32f069d1821282b430942c2197e3/image.png?expires=1784851200&signature=00b4f434af604fcec5510bcc6c623aaf6f242e6e186c502d60ad43fa4980b9a7&req=dSIjH818n4hWUPMW3nq%2BgaiPJum5mlU0I5%2B1vCxpjIpGgI%2Btx9WB0YiOBmJS%0AdOBb9QKzy5C1FeUsSFsJssudu58%3D%0A)
    
2.  Select the API documentation (i.e. [Slack documentation](https://api.slack.com/methods)). From there you should be able to identify the task you are trying to complete. For our Slack example, here's a screenshot of all the possible `chat` API calls:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300128599/af679bd68dbc97bc93ff2ee67964/image.png?expires=1784851200&signature=afe7a21f16ac387d7b58f5a6c8cab5f8dd9945f38349e853f29d86d465bf16a6&req=dSMnFsh8lYRWUPMW3nq%2BgSCq7vh237hum9HnnqLAugMqhE1o1rncs4enJdAW%0A3ntHxZwnwpsWRgwwKICxUS8Y%2BxI%3D%0A)
    
3.  Continuing with our Slack example, let's look at `chat.delete`:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300128878/513d2cfd90253c12c27c30a72de3/image.png?expires=1784851200&signature=db93c8d7abcceac454447641c61ef891e6faa274bc24e22abae6c0cffc362ce2&req=dSMnFsh8lYlYUfMW3nq%2BgYYJsEgi8EC5LCss9%2BhWB6tLM7la4SrfOThKdzwJ%0A58cHZvVrWbRDQkSt0rKm6456J%2FQ%3D%0A)
    

### ✅ Check Three: Is there a cURL example of the API call?

Platform API documentation will sometimes offer a cURL example of the API call. Whether you understand how to build a cURL command from scratch or copy it from platform docs, you can paste the command directly onto your storyboard in Tines and it will automatically create an HTTP action with that command's configuration. We call this feature [curl2Tines](https://www.tines.com/docs/actions/shortcuts/#import-from-curl). Here we can see what a cURL request example looks like.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1249428503/629de5a8c75bc4ae94a49d85eb22/image.png?expires=1784851200&signature=4112893c09db499a3ecc8bf19898f9fcf2ef3a6e040eb59a7d955b9c44b977c0&req=dSIjH818lYRfWvMW3nq%2BgTjok4io%2BQXlHucRMdk6RORFjUgK8fzxERtckAF6%0A1vtNUodY3couEC9dEHydEPSTE3M%3D%0A)

When you paste it into the story it'll look something like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1249428504/675093c15623fbb1bce1362eee88/image.png?expires=1784851200&signature=157309bddd01c8cf1a556b45ffe905fba1ef3b4b1380f454f8b0e48b673438bf&req=dSIjH818lYRfXfMW3nq%2BgYZIvUF15ySgeFROdeGztBzXLdpTZic7WGv%2BxGMV%0A%2FDIKlrZxGdUj760HuH4I9Xl8Bs4%3D%0A)

> **Tines Tip:** Make sure to double-check for any additional action [configuration](https://www.tines.com/docs/actions/types/http-request/#configuration-options) modifications required to make the API call.

### 💡 Or, build an HTTP request action from scratch

1.  Identify the URL Endpoint (i.e. look at the platform's API docs to see the URL we are making the request to). Continuing with our Slack example, let's look at the `reactions.list` [API call](https://api.slack.com/methods/reactions.list):
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300260153/bbf90d969c4dea2dc0bb434af304/QFkvbTd.png?expires=1784851200&signature=172a7e230b044dc54069aaf5d04db8cd4fe9e0519c27250d9372512cfd6dd0b1&req=dSMnFst4nYBaWvMW3nq%2BgThoxIKzw2SJnuxXaK3oRsm7BUTHNm6Kt4OB4xzj%0AUKzDLYVf0nx9z6Xpu4iZSOXg2k0%3D%0A)
    
2.  Pull an HTTP request action into your story.
    
3.  Create a unique name that describes what you're trying to accomplish
    
4.  Set the URL in the action to the URL from the API docs
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300260336/edc7e6fb10f9134eefaaada0f44e/image.png?expires=1784851200&signature=b23161c3e36f2b25f9a1fa56f0427538e41d8820bb10fabfbcc169325ee7c483&req=dSMnFst4nYJcX%2FMW3nq%2BgQaMSixQ7O1SZyhWBchdy5xvCmSCuzBCTWq7uBzN%0AajPYeiMMgH1uh4tH81vHaacFQ0s%3D%0A)
    
5.  Update the method of the action to whatever is mentioned in the API docs. In our Slack example, this API call is using a `GET` method.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300263311/ad9f936bdd1774e3a5da40401e5c/image.png?expires=1784851200&signature=b20c0e5d617cf3c7a85df51d5c0cae43cde90d05a3ee2009f89059f786e73fb9&req=dSMnFst4noJeWPMW3nq%2BgYBJsTA6sUTGWQYcZzTpUhnb8cBzyc31WNO0wggo%0A%2BH1tuzFlIdgWE1u3G2kANz%2Bu%2B9g%3D%0A)
    
6.  The parameters of an API call are the specifics of that request. Generally there will be requirements that the vendor needs in the request to provide a successful response. There are also typically optional fields that you can send over in your request. In this example we have "token" as a requirement, and "user" as an optional field we'll be adding.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300348038/adcd8506f3979e880b92048b9cf8/z0t5SF2.png?expires=1784851200&signature=7affc732c8870d21512894ee29bb9c7edcbdb606176be338a662ae30233e4296&req=dSMnFsp6lYFcUfMW3nq%2BgUbrts5AIcU4a5zVooAOaQW4ITe4v2qZEZe1JeUb%0AU0WMtpH%2FkUG0erP0ZNDQ1Z0iv6w%3D%0A)
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1300348599/482b58d458cb8ca2d9e556b07436/image.png?expires=1784851200&signature=94b843c61ba7d2bcd591c299164b79db73e138e995620d525fd88ba758f05226&req=dSMnFsp6lYRWUPMW3nq%2Bgabn724kahDn3g%2FHMa%2BtQtSHycEs%2BXUlnZ5KVkuj%0AogMR4lPMVni2%2BVEDT%2By1ls336Xw%3D%0A)
    

> **Tines Tip:** Set up your basic config, then get your API key / token for testing. This makes it so you can have most of the action configured and then get the final item of the API key. If you are unable to acquire the API key for a service, now is a good time to reach out to your team to make a formal request for it.

## Building blocks - JSON 101

## Overview

[JSON](https://www.json.org/json-en.html) (**JavaScript Object Notation**) is a text format and, in relation to Tines, is used in API calls to send and receive information. Think of APIs as a way for two systems to talk to each other. As a real-life example, a restaurant waiter (**API**) takes your order (**Client**) and sends your order to the kitchen (**Server**). JSON is the format used to make this communication clear and simple. In this article, we'll look over basic JSON structures, data types, and when to use them.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [JSON](https://www.json.org/json-en.html)
    
-   [Tines - Language description](https://www.tines.com/docs/formulas/language/)
    

## Make it happen

### JSON data types

#### Boolean

A **boolean** represents one of two possible values: `true` or `false`, and are used to represent binary choices or logical states. Boolean values are not wrapped in quotes. Think of a boolean as the answer to a yes or no question.

Example:

```
{
  "isAvailable": true,
  "isAdmin": false
}
```

#### Null

The **null** value represents the intentional absence of a value or unknown value. Null values are not wrapped in quotes.

Example:

```
{
  "firstName": "Angie",
  "nickname": null
}
```

#### Number

A **number** represents numeric values, including both integers (whole numbers) and floating-point numbers (numbers with a decimal point). Number values are not wrapped in quotes.

Example:

```
{
  "score": 89,
  "price": 19.99
}
```

#### String

A **string** is a sequence of characters enclosed in double quotes (`"`). Strings are used to represent text-based data, such as names, messages, or any value that needs to be treated as text.

Example:

```
{
  "company": "Tines",
  "mission": "To power the world’s most important workflows!"
}
```

### JSON structures

#### Arrays

An **array** is used to represent an ordered collection of values. Arrays are enclosed in square brackets (`[]`). Arrays can hold data in an ordered structure, making them suitable for lists and datasets of similar entities/values or when in need of an ordered collection.

Array example:

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

Array of objects example: This structure is useful when you have a collection of similar entities (e.g., a list of people or products).

```
[
  {
    "name": "Angie",
    "city": "Los Angeles"
  },
  {
    "name": "Yanni",
    "city": "Seattle"
  }
]
```

#### Key-Value pairs

A **key-value pair** consists of a **key** and a **value**. The **key** is always a string enclosed in double quotes (`"`), and the **value** can be any valid JSON data type: string, number, boolean, array, object, or `null`. The key and value must be separated by a colon (`:`). Multiple key-value pairs in an object are separated by commas.

Single key-value pair example:

```
{
  "name": "Tines"
}
```

Multi key-value pairs example:

```
{
  "name": "Tines",
  "isAwesome": true,
  "hobbies": ["building", "workflows"],
  "address": {
    "city": "Dublin",
    "zip": "D02"
  }
}
```

#### Objects

An **object** represents a collection of key-value pairs. It is enclosed in curly braces (`{}`), where each key is a string and is followed by its corresponding value. Objects are useful when you need unique identifiers for each element.

Single object example:

```
{
  "name": "Tines"
}
```

Multi-object example:

```
{
  "flower1": {
    "name": "Rose",
    "color": "Red",
    "type": "Perennial"
  },
  "flower2": {
    "name": "Tulip",
    "color": "Yellow",
    "type": "Perennial"
  }
}
```

Object of array example: This structure is useful when you want to group related data by categories or keys.

```
{
  "fruits": ["apple", "banana", "cherry"],
  "colors": ["red", "green", "blue"]
}
```

## Building blocks - Test actions

## Overview

There are three approaches to testing actions while building your stories. This article will explore each method and highlight the scenarios where they are most effective.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Change Control](https://www.tines.com/docs/stories/change-control/)
    
-   [Tines - Re-emit Events](https://www.tines.com/docs/events/#re-emit-events)
    

## Make it happen

We'll use the following example of a "Get User Info" action's event data that kicks off a story:

```
{
  "user": "Jane Tino",
  "email": "jtino@tines.io",
  "usernames": [
    {
      "value": "jtino",
      "is_active": true
    },
    {
      "value": "jane_tino",
      "is_active": false
    }
  ]
}
```

### ⚙️ Option one: Test mode

Ideal when needing to run a quick test, you can use Test mode with either:

-   Previous event data
    
-   New, dummy data created in the Test panel payload
    

to test without the action producing results in the Events panel. This will also ensure that no subsequent actions are executed.

Take a look at the event data for the "Get User Info" action above. Let's feed this into another action called "Pull User Email" that is referencing a JSON path to `get_user_info.body.email` in its payload to pull in `jtino@tines.io`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1323481214/8eb07b8c80289027410542e31668/image.png?expires=1784851200&signature=12885a694b8c55ef59a63252cd71ab03a8a452aed7698ce543f65bf7890e0c76&req=dSMlFc12nINeXfMW3nq%2BgfJTFCoGU7agFeXI2LFw%2BI%2F%2FmzHwtwSJVaj%2FLpe4%0At90dH64x5XnYuPTrq3JC51gjddY%3D%0A)

#### Test with previous event data

If we've already run the "Get User Info" action before, we can utilize its output event data to test the "Pull User Email" action:

1.  Click the action tile you want to test.
    
2.  Click **Test** on the action menu bar. The Test pop-up window will appear.
    
3.  Within the **Inbound Events** tab, select the event data you want to test with.
    
    1.  If you'd like to double-check beforehand, you can click **Load in editor** to see what's going to be fed into the test run.
        
4.  Click **Test**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1323502255/a3193c7c72134fcf643f54a96fe2/image.png?expires=1784851200&signature=6f8514132e20ff7b1947627e564933dc1b11049eb2da00bac2a35280730cee54&req=dSMlFcx%2Bn4NaXPMW3nq%2BgVQ3y7y2XZTP9Ralq83ZgoWKola6a25%2BYrrb8zjd%0AV33CPMCHwE0zIr2zhCZLnhqydmo%3D%0A)
    

See the output of the tested action in the **Events** tab of the Test window:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1323491196/4ed4f64032ee45118c05238ce3ca/image.png?expires=1784851200&signature=0e553acfc12eac11fbaba7327348c523f5ae49e7fc984a6c104bae27fb81d200&req=dSMlFc13nIBWX%2FMW3nq%2BgYf6JWpWR8WsyfOtQpnlm7pexFhmK3B2dVmgH6dJ%0A0d3A0s2Y5SWPHygD35BHavd%2FffM%3D%0A)

#### Test with new dummy data

If you want to test your action with value data that's different from your events, you can utilize the **Editor** tab of the Test window. How you format your test payload relies on how your action is currently configured and the upstream path it is referencing.

1.  Click the action tile you want to test.
    
2.  Click **Test** on the action menu bar. The Test pop-up window will appear.
    
3.  Within the **Editor** tab, create the payload via **Builder** or **Plain** code.
    
    1.  Make sure that the payload structure here aligns with the JSON path you are trying to test in your action.
        
4.  Click **Test**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1323497114/3873bf4f6d16484da025283d4d33/image.png?expires=1784851200&signature=f390ef0dc375d115046ae145264edadeb7519f07c7f51ad6c6b1d19ac88b957b&req=dSMlFc13moBeXfMW3nq%2BgSPApFkB%2BI5gJi%2Bk6aJE4L29EAU7l8PjnLZdQmYo%0ANjkuGuBKSb6VodZkcxT4Oww3PHs%3D%0A)
    

See the output of the tested action in the **Events** tab of the Test window.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1323500932/33be12f8598c7b60b8087a99e7c8/image.png?expires=1784851200&signature=c96e8409105e2a524dd117d93176408a3b98e5661c52f5ed8a899338f0b77500&req=dSMlFcx%2BnYhcW%2FMW3nq%2BgdtOdmplUsmtFsUEwJ3QNxStjfSkQl89%2Bhx34JWa%0AtffZ0HfVu%2FLYIuTY09ioC2fvVAc%3D%0A)

### ⚙️ Option two: Re-emit

When you need to re-execute a specific run of your Tines Story, you can utilize the "Re-emit option. This is helpful if you want to see how certain event data flows through logic that you might have updated downstream from a previous run. This is a very common method of building and testing actions as you go.

You can find the Re-emit option via:

-   Select an action tile → three-dot menu → **Re-emit last event** on the action menu bar. This will re-emit the most recent output event data from the action selected.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1323556869/63bcd87e383aef038fec4782ecb5/image.png?expires=1784851200&signature=b917d6c1469ca3356e9e5b8e8d44c126499f9f663d7eb7d41b4f1b5141313287&req=dSMlFcx7m4lZUPMW3nq%2BgTqn69p8ikZG11W%2BFpRiKGNnVWF836osG%2FG%2F1V2W%0AFp9blzvyPKmXpEiM5rIDMUeYOqs%3D%0A)
    
-   Select an action tile → **Events** on the action menu bar (or, click on the event count in the top-right) → **Re-emit**. This will re-emit the event that is currently selected.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1323553697/aca53ed361c29113e22509637187/image.png?expires=1784851200&signature=1b9c52f56ba0109b638249362fa6ed64739879249336ef9c5965bf7b9cbb521b&req=dSMlFcx7nodWXvMW3nq%2BgetzRzwvT8DwM9bRbnxqk9gH9azPRD5oQZOuYmPI%0ABolNJAUjoMtVJc3Xc3naKz%2B1oo8%3D%0A)
    

### ⚙️ Option three: Change control

In Tines, any updates to a story are applied and made live immediately by default. This approach is ideal for simpler use cases and building the initial version of your workflow.

However, for established, mission-critical workflows, greater control over changes is essential. You need the ability to experiment safely and implement complex updates all at once. This is where change control is most effective.

When change control is enabled, the "Live" execution environment cannot be edited directly. Instead, all changes are made in the "Test" environment and, once finalized and verified, are deployed to Live in a change control request. To learn more about Change Control, visit our product documentation: [Change Control](https://www.tines.com/docs/stories/change-control/)

> **Please Note**: Change control is not available on Community Edition tenants, nor is it included in our default licensing. If you are interested in adding change control to your tenant, please reach out to your Tines Customer Success Manager.

## Building blocks - Utilize the story library

## Overview

Our [library](https://www.tines.com/library/) is a collection of prebuilt story templates aimed to help you get started with designing your automation workflows. In this article, we go over how to navigate the story library, as well as how to import these templates into your Tines tenant.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Stories](https://www.tines.com/docs/stories/)
    

## Make it happen

In this video, we do a deep-dive of working with the story library. We'll also cover these topics in the sections below.

[Watch video](https://www.youtube.com/embed/LO6r8-C3hCk?rel=0)

### Navigate the story library

If you're viewing the library via our website, you can search for story templates:

-   **By team**: Filter your search by organizational departments like **Security** or **IT**
    
-   **By use case**: Filter your search by scenarios, such as **Communication** or **Employee lifecycle management**
    
-   **By feature**: Filter your search by main Tines features, such as **Workbench** or **Cases and Records**
    
-   **Community**: Stories built by fellow Tines users!
    
-   **Directory**: Our complete listing (**Stories**), or, filter your search by your 3rd party tool's name (**Tools**)
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1304740673/32c5adce8650ca1ca1fec15be359/image.png?expires=1784851200&signature=06aee29994b1882151dd0f6c18e696e6fc345d3f52b9a58da5911ac94cf2afb8&req=dSMnEs56nYdYWvMW3nq%2BgTuaV9sORTmy1gQNZqY%2BsOveG8%2FplCawxR8Wu9aI%0AYowYp07%2F4Tc0eeDb1ovSCqvCXh0%3D%0A)

If you're viewing the library via the **Story library** in the editor panel of your storyboard, you can search for story templates by:

-   **All tags**: Our complete listing
    
-   **Levels**: Filter your search by the complexity of the build (**Beginner**, **Intermediate**, **Advanced**)
    
-   **Tools**: Filter your search by your 3rd party tool's name
    
-   **Techniques**: Common build methods to optimize your story
    
-   **Tasks**: Filter your search by single-word use cases
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1304818143/23ff6ad464d3195730dff57ad9c4/image.png?expires=1784851200&signature=a97ccc63c819c1c6b79afd5ad693ac57aaffeb1dcae3b7d1cfc644e1244ec516&req=dSMnEsF%2FlYBbWvMW3nq%2Bgbfy6TXrPaEAcefg0u2YxSkeLhl1K2MnG5g9aLh3%0AycUqnAuasnqNxTXqLoP1DztlC7c%3D%0A)

### Import a template via the Tines website

To import a story template from our website:

1.  Navigate to the [library](https://www.tines.com/library/stories/).
    
2.  Find the story you'd like to import into your Tines tenant. We'll use our [Send interactive messages in Slack](https://www.tines.com/library/stories/87643/?name=send-interactive-messages-in-slack) story as an example.
    
3.  Under the story description on the left, click the `Import` button:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1304768631/bca1e24a0d01ede0315c5163f2e0/image.png?expires=1784851200&signature=3825372a4d74f6ec712db0aecb2463cdc799d83abca6a79a6f888e2cd44418f2&req=dSMnEs54lYdcWPMW3nq%2BgS3t%2BGiStAVbN1y7%2Fdp9XTcJcBc0eyNvMBinx2hd%0AdmuZ3sJjFwE94SNIqBob9zd%2BqKk%3D%0A)
    
    -   If you have not signed up for a Tines tenant yet, you will be re-routed to our sign-up page.
        
    -   If you have not signed into your Tines tenant before following these steps, you will be prompted to sign in. If you manage multiple tenants, you will be asked to select the tenant you wish to access.
        
4.  The story template will be imported into "Your drafts"; you can then move the story to your desired team, as needed.
    

### Import a template via your tenant

To import a story template from the library via your tenant:

1.  Within your Tines tenant, navigate to your storyboard.
    
2.  In your story editor panel on the left, click the **Story library**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1304791482/5430a37b99e4d7b0016ee5b2ea43/image.png?expires=1784851200&signature=35ff83acf6fc86b29496ebd90987b23b8eb2f79fdaefac304529255b35181847&req=dSMnEs53nIVXW%2FMW3nq%2BgYq7TzG1Tv52DIsNkSu8v7PJn9iR3cBmGqurln8Y%0AJS4ts4JmtbEZh0HbPnJk5Q48SGE%3D%0A)
    
3.  Find the story you'd like to import into your Tines tenant. We'll use our [Send interactive messages in Slack](https://www.tines.com/library/stories/87643/?name=send-interactive-messages-in-slack) story as an example.
    
4.  Hover over the icon next to the story; the **Import** button will appear.
    
5.  Click on the **Import** button:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1304801035/d71a264e055670602b539715ca24/image.png?expires=1784851200&signature=821ac34f5adb5da7ec0bf90555e2956bd9d612383a5bc0b744fe53adb3601fad&req=dSMnEsF%2BnIFcXPMW3nq%2BgZquA8cOPPh%2FXdngsUkv34Joby09x6%2FXbcccnLT7%0ARq9nmFyND3A6oRpJAT1dQUDlAZw%3D%0A)
    
6.  The story template will be imported into "Your drafts"; you can then move the story to your desired team, as needed.

## Build your first Tines Story

One of the best ways to get going with Tines is to follow our **[Tines Bootcamp](https://www.tines.com/bootcamp)**. You can either:

-   Follow the self-paced Bootcamp [user guide](https://www.tines.com/self-paced-bootcamp#:~:text=Overview,a%20simple%20URL%20analysis%20story.)
    
-   View the self-paced Bootcamp [video lessons](https://www.tines.com/lessons/bootcamp-lesson-01)
    
-   Attend one of our monthly instructor-led [Bootcamps](https://www.tines.com/bootcamp)
    

Or, **[book a demo](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained)** for a 1:1 chat with the Tines team.

What to expect:

-   Zero-pressure, technical demo of the Tines platform
    
-   Walkthrough of a relevant use case
    
-   Overview of products and plans
    
-   Insight into API and integration capabilities

## Can I customize by Tines domain?

Tines URL domain customization is a paid add-on. You can view our pricing overview [here](https://explained.tines.com/en/articles/7007597-understanding-tines-pricing-plans).

## Can I recover events that have expired?

No. If the period of retention has passed, the events cannot be recovered.

> **To learn more about event retention, [see here](https://explained.tines.com/en/articles/7860183-how-long-does-tines-retain-event-and-log-data)**

## Can I remove the sixty-second timeout when executing a Python script in Tines?

You can set the timeout for Run Script actions to a maximum of 110 seconds (up from 60 seconds) in cloud. The default is 10 and the maximum is 110.

For self-hosted deployments, you can override this by setting the `RUN_SCRIPT_MAX_TIMEOUT` environment variable on the Tines application. Learn more [here](https://www.tines.com/docs/self-hosting/additional-applications/run-script-for-self-hosted/#run-script-timeout-configuration).

> **👉 For more details on running Python scripts in Tines [see here](https://www.tines.com/blog/python-tines-how-to-guide).**

## Can I set up a self-hosted tenant?

You can set up Tines in a self-hosted environment by contacting your Account Executive or Customer Success Manager who can help you access the right technical resources.

Business and Enterprise Editions are available for self-hosting. Learn more about pricing and packaging [here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).

> **Note:** You can find more information about self-hosting [here](https://www.tines.com/docs/self-hosting/before-you-begin/). We also recommend sharing this document with the appropriate internal teams so they can learn more.

## Can I upload my images to Tines?

No, you cannot currently upload images to Tines.

URLs to images can be pasted and will load on your storyboard. Ensure that you use the full URL of the image.

## Carbon Black Authentication Guide

VMware Carbon Black is a cloud-native endpoint protection solution that consolidates multiple endpoint security capabilities using one agent and console. Carbon Black helps minimize downtime by responding to incidents and returning critical CPU cycles back to the business, making it an invaluable tool for keeping the world safe from cyberattacks. Here's our guide on how to authenticate Carbon Black for use with Tines.

## First, get your Carbon Black API Key

1.  Login to your [Carbon Black](https://defense.conferdeploy.net/) account. New users can sign up for a Carbon Black account.
    
2.  From the sidebar, click "Settings" and then "API Access"
    
3.  From the top right-hand corner of the screen, click "Add API Key"
    
    ![](https://downloads.intercomcdn.com/i/o/757178665/25546011f1b8413ce8268619/image.png?expires=1784851200&signature=adbda54cac4803871a71b0e3d4f9b2ff9b3e4ba3bf80642c065fd952ca984b20&req=cyUgF852m4daFb4V1XW4gc7rKDj6dL4Xl108xP5QRynD%2BhNEmTh5XvkmegdJ%0ATaoDvyUEpwzR479TtuqysCGL8w%3D%3D%0A)
    
4.  Enter the required details and click "Save"
    
5.  Copy both the "API ID" and "API Secret Key"
    
    ![](https://downloads.intercomcdn.com/i/o/759027264/f25dcd7ac7c79a8505bf7821/image.png?expires=1784851200&signature=514bb4a98339e3c66fd1a17789ccdbb43383b79debe3e7d57722b3632c6e0e47&req=cyUuFst5n4dbFb4V1XW4gT1gh4XZ1CTonLhDOJNBsvmlLx2%2BPHbsfYoKxeNV%0Ato%2FB6z3IXKnQCjpAWjHUEwlevw%3D%3D%0A)
    

## Lastly, Create a Carbon Black credential in Tines

> Two credentials need to be created. One for the "Carbon Black API ID" and one for the "Carbon Black API Secret Key"

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Carbon Black credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Carbon Black stories in the [story library](https://www.tines.com/story-library?s=carbon+black)

## Using the credential in an action

The Header configuration for Carbon Black should be constructed like the below:

```
CREDENTIAL.carbon_black_api_secret_key/CREDENTIAL.carbon_black_api_id
```

An example action you can copy and paste onto your storyboard:

```
{"standardLibVersion":"28","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Get Alerts from Carbon Black","description":null,"options":"{\"url\":\"https://defense.conferdeploy.net/appservices/v6/orgs/{ORG-KEY}/alerts/{ALERT-ID}\",\"method\":\"get\",\"content_type\":\"json\",\"payload\":{\"cb.fq.status\":\"unresolved\",\"cb.urlver\":0,\"facet\":true,\"rows\":\"10\",\"sort\":\"created_time desc\",\"start\":\"0\"},\"headers\":{\"X-Auth-Token\":\"<<CREDENTIAL.carbon_black_api_secret_key>>/<<CREDENTIAL.carbon_black_api_id>>\"}}","position":{"x":585,"y":-930},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null}],"links":[],"diagramNotes":[]}
```

## Cases: a 90-day free trial

To celebrate the first birthday of our [cases](https://www.tines.com/product/cases) product, we are thrilled to offer free access to some of our most valued customers for 90 days.

But, *that's not all!* For a limited time, you can also try our records and dashboards features for free - they make Tines even more powerful when used alongside cases. Here’s Hannah explaining a little more about the trial:

[Watch video](https://fast.wistia.net/embed/iframe/0k2alyiv7v)

### **What you need to know**

-   Your free trial **starts** on June 25th, 2024.
    
-   Your free trial **ends** on September 24th, 2024.
    
-   **During the trial,** you will have free access to cases, records, and dashboards.
    
-   We will send you a reminder towards the end of the trial.
    

Below, we try to answer some questions that you might have about this trial. But if you have any more, please reach out. We are always here to help.

### **Getting started**

First, explore the cases, dashboards and records sections of your Tines tenant. We have pre-populated those sections with some starter content to help get you get going.

Next, check out this pre-recorded 7-minute demo of Cases:

[Watch video](https://fast.wistia.net/embed/iframe/qajqk77m0h)

Lastly, if you have any questions, just reach out in the messenger below. We are here to help!

### **More about cases**

[Cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases) offers you a collaborative space to iterate on your workflows, manage requests, or track incident response.

Teams using cases today are working in a more consistent and efficient way by switching context less frequently. Through cases, they organize, take action, automate, and track what they're building in Tines.

When our customers compare cases against similar tools in the market, they find that cases offer an automation-first approach to case management. This means they’re automating the work that doesn’t need human intervention and only creating cases when they need to.

Here’s a short introduction video to the cases UI:

[Watch video](https://fast.wistia.net/embed/iframe/z5u7ty1dze)

### **More about dashboards**

[Dashboards](https://explained.tines.com/en/articles/9521649-introduction-to-dashboards) combine and display all of your cases and records data together. There are three different dashboard elements to choose from; charts, notes and lists.

1.  New charts can be created from your record data or copied from existing record reports to a dashboard.
    
2.  Lists allow you to add filtered views of your cases.
    
3.  Notes include full markdown support allowing you to provide additional detail and context to your dashboard.
    

Here’s a short introduction video to the dashboards UI:

[Watch video](https://fast.wistia.net/embed/iframe/ty6pqs9gws)

### **More about records**

You can define and structure event data across stories.

[Records](https://explained.tines.com/en/articles/7971187-introduction-to-records) are captured from the storyboard. You use records to do things like:

-   Debug a story in a consistent way
    
-   Create a system of record
    
-   Store and report on data produced by Tines
    
-   Structure relevant story run and event data
    
-   View data visually through a chart
    
-   Reference a filtered view of data from a story using cURL to Tines
    
-   Create an “alert” to be further processed or referenced later when [creating a case](https://tines.com/docs/records-cases/cases)
    

Here’s a short introduction video to the records UI:

[Watch video](https://fast.wistia.net/embed/iframe/2c4gayd840)

### **Will there be more improvements during the trial?**

Yes! We are shipping improvements all the time ([see for yourself](https://www.tines.com/whats-new/dashboard-export?category=cases)) and we’re excited for you to try the new functionality as it gets released. Please let us know what you think, we are always eager for customer feedback.

### **But, I already have a case management solution.**

There are lots of ways to use Tines cases alongside another case management solution. The real power of cases is that it can function as an integrated case management tool, or in conjunction with other case management tools. We see customers using Tines cases to take action and remediate issues within Tines, *and* using it alongside other separate enterprise case management products. Here are just some of those examples from our [story library](https://www.tines.com/library/stories):

1.  [Analyze and triage suspicious emails across tools and Tines cases](https://www.tines.com/library/stories/1184213/analyze-and-triage-suspicious-emails-across-tools-and-tines-cases)
    
2.  [Create and update Tines Cases from questions in Slack](https://www.tines.com/library/stories/1191441/create-and-update-tines-cases-from-questions-in-slack)
    
3.  [Manage Fleet compliance policies using Tines cases](https://www.tines.com/library/stories/1235728/manage-fleet-compliance-policies-using-tines-cases)
    

### **Can I extend the trial?**

Your trial by default is set to run until September 24th, and our hope is that you will be able to validate that Cases is the right solution for you during this free trial period. But if you would like an extension, just reach out to your Tines representative for more information.

### **Can I opt my tenant out?**

Yes. Speak to your customer success manager or reach out in-app using the intercom messenger.

### **Where can I learn more?**

If you’re looking to do more with cases, we recommend taking the cases course on [Tines university.](https://www.tines.com/university/tines-basics/cases)

### **What you need to know about the end of the trial**

-   You will still have access to any of the cases, records and dashboards you created during the trial. However any records and cases incorporated into stories will no longer work after the trial period ends.
    
-   You will not be able to create any new cases, dashboards or records.
    

> **Still got questions?**
>
> Get in touch. We're here to help.

## Censys Authentication Guide

Censys is a platform that helps information security practitioners discover, monitor, and analyze devices that are accessible from the Internet.

Enterprises use Censys to understand their network attack surfaces. CERTs and security researchers use it to discover new threats and assess their global impact. Censys was founded by computer scientists at the University of Michigan, and the data collected has been used in hundreds of scientific papers by researchers worldwide.

### First, get a Censys API key

1.  Login to your Censys account at [https://censys.io/login](https://censys.io/login)
    
2.  Click on the user menu and select "My Account"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850586719/3c8d34b03a8563bfb8507de9/image.png?expires=1784851200&signature=50d3ce8dddfab2d505a36d4e9093c4b2bfcbf805dcbb4f3b773c589f176d2dd7&req=fCUnE8F4moBWFb4V1XW4ga3NRnStQOM%2BdoXjkDZ0EPtY7Z0vjW8Ct68kj%2BOI%0Ahsb%2F77FQ01G7uSsGqRQoAHerVg%3D%3D%0A)
    
      
    ​
    
3.  Click on the API tab and copy your "API ID" and "Secret" to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850587422/57611609b9fdec3cbb64abc4/image.png?expires=1784851200&signature=1ddf9e515d22a1b138a88384b18593e44165f387d763ce4f6b439e62780e93b3&req=fCUnE8F5mYNdFb4V1XW4ge%2FhaNdHS%2FY0sWRkKesVOxu%2FpPrGjqhT02HERjOu%0AlOzYmX9ITVqRUfwpqA1o0tw16w%3D%3D%0A)
    

### Lastly, create a Censys resource and credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Censys resource
    
    -   **Name:** Censys API ID
        
    -   **Description:** Optional
        
    -   **Builder:** Your API ID
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    
7.  Navigate to the team that will be using the API and click "Credential"
    
8.  Click "+ New Credential" and select "Text"
    
9.  Input the values for the Censys credential
    
    1.  **Name:** Censys
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Secret
        
10.  **Optional**
     
     1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
         
     2.  **Access:** What other teams can also use the API
         
11.  Click "Save"
     

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Censys stories in the [story library](https://www.tines.com/library?s=censys&view=all)**

### Using the credential in an action

Here is an example Censys action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"36","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Search for IP in Censys","description":"Returns host information for the specified IP address","options":"{\"url\":\"https://search.censys.io/api/v2/hosts/<<ip_address>>\",\"method\":\"get\",\"content_type\":\"json\",\"basic_auth\":[\"<<RESOURCE.censys_api_id>>\",\"<<CREDENTIAL.censys>>\"]}","position":{"x":1080,"y":4440},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"37a200c25896125243268646d02f8dab9ed013982d19e734fbd96cfced31d9e9","createdFromTemplateVersion":1,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```

## Change control and story versioning

By default, whenever you update a story in Tines, your changes are applied and live instantly. This works great 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 either one complete update or multiple at a time. That’s where [change control](https://www.tines.com/docs/stories/change-control/) comes in.

## Enabling change control

For any story, enable change control with the toggle in the upper right corner.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226964847/70a85265fa725a8c9c506a1f115d/change+control+on.gif?expires=1784786400&signature=221a50bf06ab10acadd79daf24bf863580709c0b6ff7796feba80a4fa52a3b32&req=diIlEMB4mYlbXvMW3nq%2BgZbvLSYYGeqPwhny0OWfrR9NTNIYRdL7fBrMHZ%2Fc%0APFN5qAV0KZqQpqpky%2Fk3aDILkSU%3D%0A)

Tenant owners have the choice to enable change control by default for all stories in a team or tenant, while only team admins or those with [custom change control permissions](https://www.tines.com/docs/admin/user-administration/custom-roles/) can enable change control within a story.

Once, enabled, you’ll now have two working spaces and executional environments for your story – **Test** and **Live**.

Users can create drafts where they can freely make changes and test those changes within their draft without impacting the live, running story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226967521/7716d3eae08b1a97f75ee6f4a663/Create+draft.gif?expires=1784786400&signature=9ed3c71644a73669d89d4837195142b9e312ae1ca65ca349bb4f2913778934be&req=diIlEMB4moRdWPMW3nq%2BgXsXYC9%2BbP3W79zrhay3%2ByriSrjlvr3sqce%2Btn%2Bf%0AoWu6W9FBUMA4MrepBLJJSbg%2BOE4%3D%0A)

Multiple builders can collaborate on a draft, while at the same time, multiple drafts can be created in one story. Multiple drafts are helpful in a few ways:

-   Team members can work in parallel, testing different approaches to solve the same problem
    
-   Small changes can be pushed live while working on larger modifications to a story
    

### Testing changes

> NOTE: With change control enabled, it's not possible to directly edit the Live environment. Instead, you make your changes in Test, and – once you’re ready and it’s safe to do so – push them en masse to Live.

You can build inside a draft as you normally would in Tines. When testing different ideas, it may be more helpful to send external data to a test version of your webhook, page, or email action by using the unique URL in the draft story. You can also use live events in draft mode.

### Using different resources and credentials in the Test environment

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

This can be achieved by using our `META.story.is_test` 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_test, CREDENTIAL.jira_test, CREDENTIAL.jira)`

### View changes

When you are finished with updating your draft and you are ready to set your changes live, click the View changes button. The view changes modal will pop up for you to review and compare your edits to the live version.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226977261/737f0b647bd08c51b79ee7b079fd/view+changes.gif?expires=1784786400&signature=5f87b32825fc2f49bc7390ef87e0a810a88234d9a4b90558c91c11a3d57d31f5&req=diIlEMB5moNZWPMW3nq%2BgXcQMEhyvCkYSrhckFBPz3DUl3AmJQBxqro6C07B%0A5QRMeJDHOUiJWYzChZjzaXLaSK0%3D%0A)

This modal can be detached and opened in a separate window to help you review complex changes in a wider screen.

### Approving and pushing changes live

All changes must be reviewed and approved before being pushed to Live. Once you’re satisfied that your test changes are complete and validated, you can **Request a review**. (Alternatively, you can reset to the live version if your changes aren’t working out.)

Once a review is requested, a notification is sent within the Tines UI and to the change control webhook, if configured. Change requests can be approved by an Editor, Team Admin, or a user with [custom permissions](https://www.tines.com/docs/admin/user-administration/custom-roles/).

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226979662/553358bf10ca740b4585cccd97b6/push+changes.gif?expires=1784786400&signature=9fb5421121409697ad87b14540dffd1645bd8bf1cd44d269798a39f17635883e&req=diIlEMB5lIdZW%2FMW3nq%2BgeRALQsc1Fk5pC%2BW66wt7FNmGUn%2FIC9EpCEtBxSl%0ABlGC8%2FuxeYq81aZsREpuLKL4F%2Bk%3D%0A)

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.

For stories with multiple drafts, those drafts will be marked as out of date and synced with the latest live version. Though not as common, should there be any merge conflicts a modal will pop up to help you resolve those conflicts.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226984360/f0a1f72d5c2d3c840a33089e13b7/eb356e9a-be17-4d3a-814d-5d786a5a1412?expires=1784786400&signature=f2eeebb9b06d4d604f15708ba8ac648233829da1df8a46594a3456bacb90ab88&req=diIlEMB2mYJZWfMW3nq%2BgQDrBhRTJwf8pZZdH3%2B6bIW9%2BlqJd8AX8cU%2BzB7v%0ASBygPf4gQm4e8YH6ZApHyi3OtBc%3D%0A)

## View live events while in the test environment

You can include live and test data in your test environment when change control is enabled.

The "Live events" button now allows you to turn on Live events in the Events viewer.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302951644/9080e1d0e3dacd756f8896e910f9/live+events.gif?expires=1784786400&signature=c22cd4ca5a8d356f7f946ce3ed815aead4a4abca1e3abfa26796f4dcbaa7b98a&req=diMnFMB7nIdbXfMW3nq%2BgejW7bGRaXIEbe1B18bU5xGKXc5qld46cgXlFpM2%0APZAmwTNxwMAJtmnVxIRSyHgJl8c%3D%0A)

> For more information about combined events, click [here](https://www.tines.com/whats-new/combined-events-for-live-and-test-environments).

## Tips and tricks for change control

Enabling change control for your critical stories can be extremely helpful, especially when you can use it to its full potential. We've pulled together some tips on making the most of reviewing and collaborating on changes for your workflows.

| Feature | Description |
| --- | --- |
| Set-Up | Have a standard agreement on what makes a workflow mission-critical and worthy of change control. Tenant owners should define tenant-wide change control settings based on their judgment and processes.
-   For teams running sensitive or high-risk processes, [enable change control by default](https://www.tines.com/whats-new/enable-change-control-on-story-creation-per-team/) for all new stories created in that team
-   Require the change requester to be a separate user from the change approver
-   Tenant owners can also [require each change request to have a name and description](https://www.tines.com/whats-new/require-name-and-description-for-change-requests/) for all changes.

Set up [change control webhooks](https://www.tines.com/docs/stories/change-control/#change-control-notifications) to receive all change control notifications for your team.

-   Customize notifications to align with your team's processes, whether that's notifying certain people in Slack or Teams, and/or storing change control activity in a longer-term data storage product.

 |
| Building | For drafts with more complex edits, duplicate your draft and push the smaller changes live to keep a story moving along |
| Testing | Use multiple drafts to make collaboration easier and let various users test out their own ideas |
| Review | Make sure the changes you are making align with the purpose of your workflow for a more efficient review process.

-   Make descriptions and change names a requirement to properly document changes and maintain a standard across story building
-   Reviewers should understand the purpose of your workflow - descriptions will help with this!
-   Allow more builders to help with the review process with [custom permissions  
    ​  
    ​](https://www.tines.com/docs/admin/user-administration/custom-roles/)
-   To prevent any single user from both requesting and approving their own changes, activate approval requirements for all updates.  
    ​  
    ​
-   Push approved changes promptly to maintain version control

 |

To learn more about change control, read the docs [here](https://www.tines.com/docs/stories/change-control/).

## Using story versioning

> Please note: Story versioning is available on Tines [paid plans](https://explained.tines.com/en/articles/7007597-understanding-tines-pricing-plans) only.

You can access version history by clicking the dropdown menu next to the story name. You can rename, delete, clone, or export stories here.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226988445/f28916bb750b190206a6fe03d949/version+history.gif?expires=1784786400&signature=af3679ec68e88574bc56d045f661b07062b44d96fb2d3d43c38221e486979de4&req=diIlEMB2lYVbXPMW3nq%2BgcIkdgYrlc6mI9EzYhJk36pzqghIR0fUVY9JUWD%2F%0AcWSvPt5p0C7AzyT64N3bHs4qXRE%3D%0A)

### Preview

To preview a previous version of a story, select from the versions available and select the preview option.

Preview allows for a review of the overall story and inspection of specific changes. This can be helpful if you wish to review or recreate a specific action's configuration after you make a change.

### Revert to a previous version of a story

To access previous versions of your story, click on the three dots on the top right of your story and select **Versions**. You can then select a previous version of a story.

From here, you can view the changes made from the previous version and decide whether to restore them or not.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2226990170/81df9d9dbc3da6ff189effd119b6/restore+changes.gif?expires=1784786400&signature=75f8167a1bc53466333b3852b39d700f9a30b46262e18f6265cf833a42f831b4&req=diIlEMB3nYBYWfMW3nq%2BgWv%2BRVmXcc55WrcF1cBBSBwQ7J4j%2B7sdbSG5wCUK%0AELggZDduxplQIkWRrr0arDjQoEY%3D%0A)

### How long are story versions retained?

[Story versions](https://www.tines.com/docs/stories/story-versioning/) are retained indefinitely unless the story is deleted from the tenant.

> To learn more about story versioning, [see here](https://www.tines.com/docs/stories/story-versioning).

## Checking self-hosted tenant details

To check your version of self-hosted Tines, use https://yourtenanturl/version

### How can I check my Tines self-hosted tenant's health?

You can use the following to check the health of your tenant. This applies to both self-hosted and cloud Tines: [https://yourtenant.tines.com/health](https://yourtenant.tines.com/health)

For self-hosted tenants, you can also check what data is being shared with Tines hourly by using the following: [https://yourtenant.tines.com/admin/telemetry](https://yourtenant.tines.com/admin/telemetry)

### How do I update my license key on my Tines self-hosted install?

> **Note:** Only a tenant [owner](https://explained.tines.com/en/articles/6885158-user-administration-in-tines) can set the license on your tenant.

1.  Visit [https://your-tenant-url/admin/license](https://your-tenant-url/admin/license)
    
    ![](https://downloads.intercomcdn.com/i/o/744379861/86025d516fc22e112e9ed6d4/10d75402-aa5e-473e-b546-2cd6564f8444?expires=1784851200&signature=367e3bd785d4e4634d856d095fd0404ba6eddf8b592b98d1df76569db5a3154f&req=cyQjFc53lYdeFb4V1XW4gXs5OXI9AHjvXHZ5A7cRBYLXuvjHXgee6PQ%2BZTwD%0AemL4dS%2BOcWAyRpVNFFj3KbK6hQ%3D%3D%0A)
    
2.  Paste in your license key provided by TInes and click “Validate License”
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810274237/993f8413fd33cade7639b138d2bf/96152aaf-1860-48aa-9cce-56ed6c259708?expires=1784851200&signature=56a5edc4152cfca1702365865277f530cadd029881e5006686bf89057e4608f1&req=dSgmFst5mYNcXvMW3nq%2BgTD3eFnMDLkZAmf71CYAW%2BiK%2B9SuFri0ZhrHykIy%0A1%2BQx5PBEVufET9mKvi4wUriPJl8%3D%0A)

3\. Ensure that all details on the validation page are correct. Correct values match the features and limits you purchased. Please ensure that the license is being applied to the right Tines environment.

​

4\. Click “Save New License” to save the license and apply your tenant features.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810274235/cde741e6d7ab0dc7875fbe3e402b/16b23677-34dc-4b93-9806-b074b56d198e?expires=1784851200&signature=4579dc201cc156384c0d59eaa4f0815556065aa3026926b3e1f52fa6b9dd8c29&req=dSgmFst5mYNcXPMW3nq%2Bgc3263XLqOA4uofsdFlazeQP1GtMfSmC5j0xaVQZ%0AIjUgS4YF01Zr3th1xj3Ry7DeAp0%3D%0A)

## Choosing the right function: Best practice guide

When building stories in Tines, selecting the correct function to transform your event data is crucial for efficiency. Just like how spreadsheet formulas help you manipulate data in cells, Tines functions help you transform, combine, and manipulate data as it flows through your workflows. This guide will show you the different methods for choosing the right function to use.

Need a refresher on functions? Read [this article](https://explained.tines.com/en/articles/9009911-functions-in-tines-explained) on functions in Tines or check out the University course [here](https://www.tines.com/university/builder-core/introduction-to-functions/).

## Tactical planning for data transformation

Before getting started, it is helpful to plan your data transformation logic.

-   Define your goal in plain language: Determine exactly what you want the data to look like at each step.
    
-   Translate to plain language: Write down the action you want to take, for example: "I want to remove any data that comes in that is inactive," or "I need to change the timestamp format to this specific format".
    

This initial planning process will help you clarify your requirements before you engage with the documentation, community or our AI tools, ensuring you find the most suitable function.

Here are our recommended approaches for identifying which Tines function to use.

## AI-powered assistance

Leveraging Tines' AI tools is the most efficient way to find the correct function, and some options are AI credit-free.

If you need help learning any of our AI features, check out our [Builder: AI](https://www.tines.com/university/builder-ai/) learning path in University.

> In all of these examples, we will be prompting AI to “remove any objects from the array that have a status of inactive”.

Here is the data we’re transforming:

```
[
  {
    "id": "1",
    "status": "inactive"
  },
  {
    "id": "2",
    "status": "active"
  },
  {
    "id": "3",
    "status": "active"
  }
]
```

### Auto generate

Available within a formula pill, [auto generate](https://www.tines.com/university/builder-ai/auto-generate-formulas/) allows you to use plain language prompts to create functions.

Auto generate automatically places the formula into your action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2200589872/ef6e8fc952e703debe6d9965c9af/pill+function.gif?expires=1784786400&signature=8643f2f1469ad45428be067bd79e80a1389b94435a29e4956b17e299dd0996c1&req=diInFsx2lIlYW%2FMW3nq%2BgYPVJtCozc8CHFxlvNqAi1C5dZ9fq1mFbBiLig2a%0AGOZaHn%2BG5B5f0dgEz62yfg%2FyIGQ%3D%0A)

### Automatic mode

[Automatic mode](https://www.tines.com/docs/actions/types/event-transformation/automatic/) is one of the modes available within the event transform action.. It allows you to use plain language to build the action for you.

Automatic mode allows you to see the output before saving.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2200589342/0e225a8efecd5c8cbc06aadc3275/automatic%2Bmode%2Bformula.gif?expires=1784786400&signature=8b4f70129f3f5921804316b6734c8d4f8f13b262515b75a9ba66aeb39b46bb86&req=diInFsx2lIJbW%2FMW3nq%2BgcBmhdB2r53jZ40XwW0nNArMp2tp1X8OJIG6Try1%0AVFYL22dikV06rRbrOvqv5SG0hBM%3D%0A)

> Note: If the initial result isn't perfect from either auto generate or automatic mode, don't be discouraged; you may need to make minor adjustments to your prompt to get the desired result.

### Workbench for Storyboard

Workbench for Storyboard (formerly Story copilot) is an AI tool that can suggest methods for function generation. There are two modes you can use here: Ask and build mode.

Read more about Workbench for Storyboard [here](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot).

> Note: Workbench for Storyboard can see your story build, but it cannot see the event data flowing through your workflow.

In our example below, we use Workbench for Storyboard in build mode so it is able to implement the changes in our story for us.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2200592021/ef9cd66a874166b993c4078a9ba3/story+copilot+function.gif?expires=1784786400&signature=81cef7fa1aef5712a427ce84558b6c9a2da0937002f39914cbc52a6273235aaf&req=diInFsx3n4FdWPMW3nq%2BgZovGQ4%2B6bvWYiLavzNKMzy0CqkDiPPtHPtaC%2F5i%0A8XN2VEsPi57UWptd7Blzi9SoHMI%3D%0A)

### Workbench

Workbench is a Tines-powered AI chat interface that allows you to take action and interact with your data in real-time. Read more about Workbench [here](https://explained.tines.com/en/articles/9822635-introduction-to-workbench).

> Note: Workbench doesn't have contextual knowledge of the story/action they're working with.

Tips for using Workbench to choose the right function:

-   Provide your ask in plain language.
    
-   Provide an example of the incoming event data structure.
    
-   Provide an example of the desired result.
    

## Manual or community resources

If AI assistance is not an option, or if you are looking to learn more about the functions, you can use manual and community resources:

### In-app chatbot

You can use the in-app chatbot, Fin, to receive guidance on the correct Tines function.

For example, you could ask Fin “I have an array of objects where I need to remove any objects that have a status key of inactive. What Tines function should I use?”

Fin could then suggest functions like [`REJECT`](https://www.tines.com/docs/formulas/functions/reject/) or [`FILTER`](https://www.tines.com/docs/formulas/functions/filter/) when you need to process an array of objects.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2200594889/2bdb532207075e148b1b3739f078/Open+Fin.gif?expires=1784786400&signature=27ac5120cca4b541dfaaa2c7cc7ea3d2720f928770efb532b91efbba6d7c228e&req=diInFsx3mYlXUPMW3nq%2BgYUI55BwSwaFAXh5U1asGaqztCAgSFEMQS9Hiws6%0AbCWld1Zd1JywxoUSCEwB0boIH3A%3D%0A)

#### Best practice for Fin

Since Fin doesn't know your specific data, you should provide specific input, such as a plain language description of the action you want to perform or the expected output, to help it provide the best response.

### Function documentation

The function documentation lists all available functions and provides a summary of what each does. You can find the list of all functions available in Tines [here](https://www.tines.com/docs/formulas/functions/).

Clicking into any of the functions gives you a description, syntax, and usage examples.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2200595755/df0e8edb744acab14cf9c5563022/c57d5a68-c85f-4082-b8ee-c58a474d07ce?expires=1784786400&signature=d2a19ae7188fdcff513f9b15328445664c9c2bad7997bbb0b1492f0999e5cbd2&req=diInFsx3mIZaXPMW3nq%2BgVaNO6TGer5i8qlg%2Bg1JP%2FZGUdbQRhNma75DRlUu%0A8Kj1GaFYC7IhwjWVO63zmCImD2k%3D%0A)

### Community Slack

You can ask for help from the Tines community in the Tines Community Slack channel.

To register for the Tines Community Slack, follow the instructions in [this form](https://hq.tines.io/pages/6f8b122ccba3cb7e8e0d3531d1b70eb2/).

## CircleCI Authentication Guide

CircleCI is the continuous integration & delivery platform that helps the development teams to release code rapidly and automate to build, test, and deploy. CircleCI can be configured to run very complex pipelines efficiently with caching, docker layer caching, resource classes, and many more. After repositories on GitHub or Bitbucket are authorized and added as a project to [circleci.com](http://circleci.com/), every code triggers CircleCI runs jobs. CircleCI also sends an email notification of success or failure after the tests are complete.

## First, get a CircleCI API token

1.  Login to your CircleCI account
    
2.  Click on the user menu and then click "User Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948713217/19089121e58b17d6aff4412a/image.png?expires=1784851200&signature=9fe10a0383d2acfa247fc32ba1f418ff813b2265106758f673b73aa13ffb9f73&req=fSQvEch9n4BYFb4V1XW4gWL%2BG%2BlYXNech2KStCiYRfOmjDpy9NifC16nO472%0Ay3mPptEkWRPHQnbgAJwJlNYuYQ%3D%3D%0A)
    
      
    ​
    
3.  Click "Personal API Tokens" and then click "Create New Token"
    
4.  Name the token and click "Add API Token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948714091/5d2d2709625b83946ac0a4a7/image.png?expires=1784851200&signature=a37fd939d2c464c78d7bb31bd3d508500fbefe7b22b05863effc38d44a7e8d42&req=fSQvEch6nYheFb4V1XW4gSFQQ5gnEZ%2F4MfanYhBnREWslfY4Kfyvlujn%2FQy%2F%0AIGKGE6gtekgUAepUo52SF58zwQ%3D%3D%0A)
    
      
    ​
    
5.  Copy your API token to a text editor.
    

## Lastly, create a CircleCI credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the CircleCI credential
    
    1.  **Name:** CircleCI
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of CircleCI** **stories in the [story library](https://www.tines.com/library?s=circleci&view=all)**

## Cisco Umbrella Authentication Guide

**Cisco** **Umbrella** is cloud-delivered enterprise network security that provides users with a first line of defense against cybersecurity threats. Here's our guide for authenticating Cusco Umbrella for use with Tines:

## First, get your Cisco Umbrella API Key

1.  Login to your [Cisco Umbrella](https://login.umbrella.com/) account. New users can "Sign-up" for a Cisco Umbrella account if they do not already have one.
    
2.  From the sidebar, click "Admin" and then "API Keys"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/757236572/0ccef2428b0a6bba98b7acf4/image.png?expires=1784851200&signature=835b67d5e1494e1435e3df083b310e03e37a976272981f8a7b8c4a6f7c5b6c05&req=cyUgFMp4mIZdFb4V1XW4gYzrhc%2FVUnk4eH1Y3skoZT%2FsPCDwPXDwRLQKR7HX%0AOeJIu%2FT3svwnHYHM42OoWIyZqw%3D%3D%0A)
    
3.  From the top right-hand corner, click "Add"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/757237065/f6790ae94793834f1c0d3c37/image.png?expires=1784851200&signature=8ba0bbebf5507221f1442487fe01e499e20ea17f935029cbb5c8e32e7bd5279e&req=cyUgFMp5nYdaFb4V1XW4gcT8GayeXJejWov%2FUaXSV0QGgk9mzkUT3Ij%2F%2FXuk%0AU49qaHwyYqtD9qrT92CZJbo6aw%3D%3D%0A)
    
4.  Fill in the required fields, click "Create Key" and then copy the API key and the Key Secret
    

## Then, create a Cisco Umbrella credential in Tines

**Cisco Umbrella connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Cisco Umbrella" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Cisco Umbrella connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click **Credential**
    
3.  Click **\+ New Credential** and select **HTTP Request**
    
4.  Input the values for the Cisco Umbrella credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
5.  Scroll down and select **`Edit as JSON`** and copy the below payload in:
    
    ```
    {
      "url": "https://api.umbrella.com/auth/v2/token",
      "content_type": "application/x-www-form-urlencoded",
      "method": "post",
      "payload": {
        "grant_type": "client_credentials"
      },
      "basic_auth": [
        "YOUR API KEY",
        "=secret"
      ]
    }
    ```
    
6.  Select **Done**
    
7.  Scroll down to the section titled "Basic Auth" and replace "YOUR API KEY" with your API key.
    
8.  Scroll down past the "Basic Auth" section to the section titled "Secret" and paste in your API key secret. You'll note the "Basic Auth" section references this field, but you will paste in the secret in the "Secret" section to keep the key hidden.
    
    ![](https://downloads.intercomcdn.com/i/o/1011024784/bca819a6d2bbe0b2d7542ade/http-req-secret.png?expires=1784851200&signature=1dcf92a3e5b863dc14e5c139c8768d7fcb43f83679745f7dbd2a845b97fd537f&req=dSAmF8l8mYZXXfMW3nq%2BgUFKe3Y%2BkSgTAxMWGtPT7V52fhy2jEL32pLIffX%2F%0AvpKzxaUrHOjMTNajO10d%2F3HcyvA%3D%0A)
    
9.  In the `Domains` field, enter `api.umbrella.com` to restrict this credential to only being used in requests to that domain.
    
10.  You're not done yet! Select **Save** for the Credential to save and re-open.
     
11.  Scroll down and select **Run request**. This will run the HTTP request and show the results in JSON. If you successfully configured your HTTP request credential correctly, you will see a 200 status returned.
     
12.  Copy the path to the access token by selecting the copy icon next to the key (see picture below) in the successfully run request and paste the value in the section `Location of token from response`. This will be referenced each time an action is run in your workflows that calls this credential.
     
     ![](https://downloads.intercomcdn.com/i/o/1011030111/eeb6b2d997f6e20738e89ee7/copy-access-token-path.png?expires=1784851200&signature=7a39c04a9bddcc71598451c584acb8f11d30d31cf276d73c71b09484271fde71&req=dSAmF8l9nYBeWPMW3nq%2BgXpIFnOebuOpN7iZJKCqKGM4%2F6wg%2Fn399GUtJYKh%0A3Y7umbnKDYTKgHnNwvPb%2B2PYa7U%3D%0A)
     
13.  **TTL**: Set to 3500
     
14.  Select **Save**
     
15.  **Optional**
     
     1.  **Access:** What other teams can also use the credential
         

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/cisco-umbrella).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Cisco Umbrella stories in the [story library](https://www.tines.com/story-library?s=cisco+umbrella)

## Claroty xDome Authentication Guide

Claroty empowers organizations to secure cyber-physical systems across industrial (OT), healthcare (IoMT), and enterprise (IoT) environments: the Extended Internet of Things (XIoT). The company’s unified platform integrates with customers’ existing infrastructure to provide a full range of controls for visibility, risk and vulnerability management, threat detection, and secure remote access.

## First, get a Claroty API key

1.  Login to your Claroty account
    
2.  Click "Settings" and then "Admin Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866621717/77bc16d884e0830b7fdcf691/image.png?expires=1784851200&signature=919f3734d7fdd7579067571f1e82f33f24db368907d346a4c3f5c4505241ce89&req=fCYhEMt%2FmoBYFb4V1XW4gTGOmMULbNIRudCbAAghW8PxyBU6eE3pYqV8p5Kg%0AScQc20KrPPkwh60wlKQZqV1gsQ%3D%3D%0A)
    
      
    ​
    
3.  Click "Add User"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866629764/fd4912c11b02725e57debeac/image.png?expires=1784851200&signature=c4b451e85996782489c31f33cc7fcff2d1632c28865f29155f8e4c17355531d3&req=fCYhEMt3modbFb4V1XW4gUCjDR3e0d3DhRD7eFYd%2FHiHS0fFrY%2BdA7SzdGdK%0A6n7ELtG3rJqRorQd34ZLH%2F40VA%3D%3D%0A)
    
      
    ​
    
4.  Choose "API User", name the user, choose the appropriate site permissions and role, and click "Create User"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866630309/aa1b1c6c4f2c2c7bfa8d56f7/image.png?expires=1784851200&signature=9b041282eba394cd1eb15064237aee7e29115bd3121326b888e3c252c896a2b7&req=fCYhEMp%2BnoFWFb4V1XW4gdqZgtpW0YsgR1%2FQYVtXXaGNMD3zcCrqMiQyWBfe%0Abw3gA15rcag0dVfMBNMWcQGj3A%3D%3D%0A)
    
      
    ​
    
5.  Click "Generate Token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866632618/237b86d6d989d6a2b3195204/image.png?expires=1784851200&signature=06bb05df30431d2c5c9f71437ddde419101cda4ad6dd05711fea308d37c00b7a&req=fCYhEMp8m4BXFb4V1XW4gY1HLrObazj9Qrlhl9KTzfNmpi4zD8T86nwi84Zx%0ADHOa8drdK9eY3qnkAPk97QryoA%3D%3D%0A)
    
      
    ​
    
6.  Choose the token expiration, click "Generate", copy the API token and click "Finish"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866634950/dfb6e253df86ad7929765820/image.png?expires=1784851200&signature=e4ea077812aa916f8451076e67a9458a6c7740344b24997b10aab76793017cc5&req=fCYhEMp6lIRfFb4V1XW4gTz6YyIYQIPL8TV8%2FqxUMyX0WMvBGXrHgqpdTg4N%0ACm9RIy0b2OB5l3QleU%2FhauX%2BLg%3D%3D%0A)
    

## Lastly, create a Claroty credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Claroty credential
    
    1.  **Name:** Claroty
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Claroty** **stories in the [story library](https://www.tines.com/library?s=claroty&view=all)**

## Coming soon: new storyboard toolbar

Here is a first look at the brand-new, fully customizable toolbar coming to the Tines storyboard.

## What’s evolving?

The 8-action toolbar has served us well for years, but Tines has grown. To make room for more power and less clutter, we’re introducing:

-   **Full customization**: You’ll be able to drag and drop your most-used actions and public templates directly into the toolbar, allowing you to start building faster.
    
-   **Better discoverability**: We’re organizing all building elements into a single toolbar with more intuitive groupings, making it easier to find what you need.
    

> **Note**: The way actions work is not changing, and your stories won’t be impacted by this update.

## When will this happen?

You don’t need to do anything yet! We’ll be rolling this out to all tenants on April 28th. We’re sharing this now so you can be aware of the plans before the big day.

## How will it look?

Below is a sneak peak at the new design. In the video, you will see:

-   A view of the old vs new toolbar
    
-   The new quick search option
    
-   Dragging and dropping actions
    
-   Searching templates
    
-   Saving and removing templates and actions in the toolbar
    

[Watch video](https://fast.wistia.net/embed/iframe/iksknpi0fe)

## Why the change?

As we add more functionality to the storyboard (and more ways to build), we want to ensure you are aware of these new releases and able to easily find them. This update is all about discoverability and speed.

## Comparing AI in Tines

This document provides clarity on the distinct and powerful value propositions for each AI capability within Tines. For example: Why use Workbench when you have Chat Agents? When should you use Workbench or Workbench for Storyboard?

## Workbench

A Tines-powered AI chat interface where you can take action and access proprietary information in real-time, privately and securely. Workbench is like your ChatGPT or Gemini scoped to the available data and workflows in Tines.

### Use cases

-   Looking up and editing data in a system (i.e. Salesforce)
    
-   Work on an incident or ticket request for your team
    
-   Analyze & Block IPs, domains, URLs
    
-   Generalized AI help such as content creation, formatting, suggesting next steps
    

Read our introduction to Workbench [here](https://explained.tines.com/en/articles/9822635-introduction-to-workbench).

## AI Agent action

One of the eight actions in Tines. It operates within the context of a story or flow that interacts with LLMs and the data associated within that specific flow. This includes task and chat mode (human-in-the-loop).

### Use cases

-   A chat for ticket management and inbound requests to route to the correct story
    
-   An agent that triages incidents then routes them to the right story
    
-   Summarizing data into the right case format
    
-   Suggesting next steps or recommended actions for a case
    
-   Executing next steps or actions based on the story and prompt permissions
    

Read our introduction to AI Agents [here](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents).

### When to use AI Agent chat or task mode

AI Agent chat is applicable when you want to create a chat experience to initiate the agent. They are great for situations that benefit from a back-and-forth conversation. AI Agent task gives the agent autonomy to move based on the defined input. They are for more straightforward actions that don’t need user input.

## Workbench for Storyboard (formerly Story copilot)

An AI-chat interface in the storyboard to help you build, manage, or optimize the flows on that storyboard.

### Use cases

-   Getting a story outline on the storyboard
    
-   Configuring action descriptions
    
-   Debugging a Story
    
-   Configuring new action templates or tools
    
-   Updating credentials
    

Read our introduction to Workbench for Storyboard [here](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot).

## Comparisons

### Workbench vs. AI Agent chat

Workbench can be used by people with access to the Tines tenant. It’s not scoped to a specific flow or storyboard. Users of Workbench need to be a member of your Tines’ tenant. Agent chat is limited in scope and has a defined set of permissions. However, people outside of your tenant can engage in an agent chat.

A simple way to view it:

-   *Workbench helps you in the work going through tasks.*
    
-   *Agent chat is specific to a task and scope of work.*
    

### Workbench vs. Workbench for Storyboard

Workbench is best used for tasks other than story building because it lacks the innate context of your specific story. Workbench for Storyboard is an AI-powered chat interface to build, understand, and optimize flows.

A simple way to view it:

-   *Workbench helps you do your non story building work.*
    
-   Workbench for Storyboard *helps you build, which includes optimizing, managing, and understanding.*
    

### Workbench for Storyboard vs AI Agents

Workbench for Storyboard is an AI chat interface in the storyboard to help you build, manage, or optimize flows. AI Agents are an individual action within a flow that performs tasks or engages in end-user chat interactions.

A simple way to view it:

-   Workbench for Storyboard *helps you design and optimize the entire flow on the storyboard.*
    
-   *AI Agents are a functional part of the flow, performing tasks or engaging in conversation with customers*

## Comparing Tines managed AI providers

Tines offers two managed AI providers: AWS Bedrock and OpenAI. Both are available with no API keys or setup required — Tines manages the infrastructure so you can start using AI features straight away.

Read about AI usage and credits in Tines [here](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits).

## What both providers guarantee

Regardless of which provider you choose, Tines handles your data using the same strict security and privacy rules. Both AWS Bedrock and OpenAI adhere to these core principles:

-   **Stateless** – No conversation state is retained between requests. Each interaction is independent.
    
-   **No training** – Your data is never used to train AI models.
    
-   **No storage** – Data is not stored by the AI provider. Any persistence happens within Tines.
    
-   **No logging** – Requests and responses are not logged by the provider.
    
-   **No setup required** – Both are Tines-managed. No API keys or configuration needed.
    

## Key differentiator: Data routing

The primary difference lies in how your data is routed, which is crucial for organizations with strict residency needs.

### AWS Bedrock

Requests are processed in the same geographical area. I.e., data within the EU stays within the EU. The data never travels over the public internet and instead uses AWS's privatelink.

### OpenAI

Requests are routed to your deployment region (US or EU) by default, then sent to OpenAI for processing and returned to Tines. While data travels to OpenAI's infrastructure, it is not stored or used for training, and routing respects your regional deployment.

## Side-by-side comparison

|  | AWS Bedrock | OpenAI |
| --- | --- | --- |
| Setup required | None | None |
| Data routing | In-region | Deployment region (US or EU) |
| Stateless | ✅ | ✅ |
| No training | ✅ | ✅ |
| No storage | ✅ | ✅ |
| No logging | ✅ | ✅ |

## Choosing the right provider

Both providers deliver strong privacy guarantees.

If your organisation has strict data residency requirements where data must not leave your regional infrastructure under any circumstances, AWS Bedrock is the recommended choice.

If your requirements are more flexible, OpenAI provides access to the latest OpenAI models and uses your existing AI credits across both OpenAI and Anthropic models, while still adhering to Tines' core privacy standards.

Tenant admins can switch between providers or revert to the Tines default at any point in AI Settings → Providers.

## Condition FAQs

## Are rules checked in a particular order in a condition action?

If you have multiple rules as part of a condition action, they are not checked in any particular order.

By default, all rules must match for the action to emit an event. If you wish to match only a subset of the rules, for example, two of three rules, you can set that value in must\_match.

## If I use a condition with multiple rules, do all rules have to be matched or just one?

A condition with multiple rules acts in an AND fashion. All rules must be satisfied for the action to work.

You can use “must match” in an individual condition action to specify “OR” For example, using must match 1 acts as an OR.

Read more about conditions in our docs [here](https://www.tines.com/docs/actions/types/trigger/).

## Conditions in Tines

Conditions are actions which compares the content of a field from an incoming event with predefined rules, when the rules match, an event emit is triggered. Conditions in Tines are your automation's starting point, the digital equivalent of saying "when this happens, do that."

![](https://downloads.intercomcdn.com/i/o/am0g137l/2179578097/e13ac86f8fdb216b8c07aa7ec833/image.png?expires=1784851200&signature=7f9246d9b3b6899a29b4cb1cf61d43c100596ad74f30575b1e50ebf7efd23106&req=diEgH8x5lYFWXvMW3nq%2Bgfu%2BAXCGiKmvNlpkrs4lqNCkeW0c1Dzo8GS9Wjzf%0AHjsjuKA1nLLry9LBwVys2gAxRoY%3D%0A)

Some common use-cases for the condition action include:

-   Ignoring Events that don't require processing
    
-   Send Events downstream for additional analysis
    
-   Further process events that are sent from a particular application
    

### Multiple rules

A condition with multiple rules acts in an AND fashion. All rules must be satisfied for the action to work. You can use “must match” in an individual condition action to specify “OR” For example, using must match 1 acts as an OR.

If you have multiple rules as part of a condition action, they are not checked in any particular order. By default, all rules must match for the action to emit an event. If you wish to match only a subset of the rules, for example, two of three rules, you can set that value in must\_match.

### No match

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.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2179593580/dda723e7e18f475866983d4fea8e/image.png?expires=1784851200&signature=32e63bc32ca4c066d3f539bdbfbef1f1ba7f62f06b0d7260a703ddb299368e3a&req=diEgH8x3noRXWfMW3nq%2Bgf%2Bu9Q%2F2AWuJj9CMZZoQqVzWy9bqMvCnTaKd6aTn%0AAkwUWwhONE%2BtqK8znUvBeksIX2E%3D%0A)

> **👉 Learn more about conditions [here](https://www.tines.com/docs/actions/types/condition/).**

## ConductorOne Authentication Guide

ConductorOne helps companies secure identity in their cloud apps and infrastructure through centralized identity governance and access controls.

## First, create a ConducterOne API client

1.  Login to your ConductorOne tenant
    
2.  Click on your user account and select "API Keys"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/859330071/f3b618529d8c319f347d5bf9/image.png?expires=1784851200&signature=5b4d97bfbb17e11929e1b9d9dbe7dd8a77690bf329d630a2b2c1fdc592df0546&req=fCUuFcp%2BnYZeFb4V1XW4gXsrNaRuzYzu%2F0D9AwcPMqN4ii4QRN3zcNPgygIH%0AXf0OYJ%2B0BPrM2ULq%2Fpu3ZEyXfw%3D%3D%0A)
    
      
    ​
    
3.  Click "Create credential"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/859330305/c9ed20541fd88f35858a8bd0/image.png?expires=1784851200&signature=b9aad9c2ac173c996dc2728101a5747c66521026e10c787c0088d8e990cab999&req=fCUuFcp%2BnoFaFb4V1XW4gTivWn%2FxEU6Fxs8TxKNtpItUYCtx8XBvi01aKSao%0A%2F5a%2FfTbBvcNJzQb5IWGHp52Emg%3D%3D%0A)
    
      
    ​
    
4.  Name the client, set the expiration date and click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/859330745/95d2caf169c8b03c96d25993/image.png?expires=1784851200&signature=74ab13542a611307d1772293f243096482a8120cead780fa3210fd75687c0355&req=fCUuFcp%2BmoVaFb4V1XW4gcEeUFGGfWw3khDKQrZeWEDEsDMURkNSN4iCbKuo%0AUdFwv6o7%2FCOGgTIZBYhmtxpr9g%3D%3D%0A)
    
5.  Copy your "Client ID" and "Client Secret" to a text editor and click "Done"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/859331110/baa353fc3b933476f9687148/image.png?expires=1784851200&signature=a40a650d13593773db0e4d6ec5a9db89e41075b20d92711e5b4c81bafeff2a1d&req=fCUuFcp%2FnIBfFb4V1XW4gUs%2FJ%2FH2uh3Fw%2BjQpFnIlJrruZKTkmW0zGGeMA6g%0As3OkWVOpESMf4mcpAe7ny%2F%2Fw3A%3D%3D%0A)
    

## Lastly, create ConducterOne credentials in Tines

You will need to create three credentials:

-   Two Text type: "conducterone\_client\_id" and "conducterone\_client\_secret"
    
-   One HTTP request type: "ConducterOne"
    

##### Text type

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the ConducterOne credential
    
    1.  **Name:** "conducterone\_client\_id"
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Client ID
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    
7.  Click "+ New Credential" and select "Text"
    
8.  Input the values for the ConducterOne credential
    
    1.  **Name:** "conducterone\_client\_secret"
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Client Secret
        
9.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
10.  Click "Save"
     

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the ConducterOne credential
    
    1.  **Name:** ConducterOne
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<tenant>.conductor.one/auth/v1/token`
        
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste it into the "Plain code" section:  
        ​
        
        ```
        {
          "grant_type": "client_credentials",
          "client_id": "<<CREDENTIAL.conducterone_client_id>>",
          "client_secret": "<<CREDENTIAL.conducterone_client_secret>>"
        }
        ```
        
    7.  Click "Run options", navigate to the `access_token` key and double-click on the key  
        ​  
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/859332836/5a07ddbfefd9a2854b2af3af/image.png?expires=1784851200&signature=8a208f8df1a88fdb1299b9a3e5d43a21622e14184f422f1d7267d9c9a747f121&req=fCUuFcp8lYJZFb4V1XW4gSdIZ9KITWMD7idD7190cUcsa%2BxLfcccdQ%2F9h7bI%0Ajk3Jv3O%2FiS%2FqZSEKby9R1Sh%2F6A%3D%3D%0A)
        
    8.  **Location of token from response:** Paste the path to the token in the value pill
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/859333826/5a52f47cb606df9cf89763b9/image.png?expires=1784851200&signature=e1dd7121b03d1f562826f66660148cab28a9a73a0f4fc6c61dc9b2075b7947c4&req=fCUuFcp9lYNZFb4V1XW4gTTED9aRiQBT3WFneTeKspo3TbK6gYI19Prx1P0P%0A8%2FjrEWlHjESwIAThX3X7e0ortQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/859333866/944e5c52c15ee67054d9928b/image.png?expires=1784851200&signature=c12ed8afab1d24afacc0880652df3532d24dcb66bf853c46f448b02ea772505c&req=fCUuFcp9lYdZFb4V1XW4gWXrd74DY9u%2F8damTtJW269ilcmtF%2FsvopSldhze%0A5SA1eF13vrs5kPWbeo0HtNctnQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/859333933/e679e272f8e2b6b44d0fa9f5/image.png?expires=1784851200&signature=d242a444ae0312e0a5a185fd8b9a458d3d5c54e2749dc9ed1cc35b38629d7abc&req=fCUuFcp9lIJcFb4V1XW4gdRmnEEQuTMZPZkbvN%2Bw1vTnWKiEQ%2F%2FdV9%2BNqkVl%0A%2B316wTwXClaQf0Uf6a18bujC0Q%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of ConducterOne stories in the [story library](https://www.tines.com/library?view=all&s=conducterone).**

## Configure Amazon EventBridge for Security Hub Findings

Tines integrates directly with AWS Security Hub findings by receiving events sent through Amazon EventBridge. When Security Hub generates or updates a finding, EventBridge routes that JSON payload to a Tines Webhook, where it can be parsed and used to trigger downstream workflows. This allows teams to process findings in near real time using a combination of deterministic steps, human review, and agentic actions.

Security Hub provides the centralized visibility and prioritized insights across AWS environments, while Tines extends that signal into the rest of the security and IT ecosystem. Through Tines, teams can enrich findings with external context, open or update cases, request approvals, notify stakeholders, and orchestrate remediation across AWS as well as third-party systems — without maintaining custom integrations.

Together, Security Hub and Tines enable consistent, repeatable handling of cloud security events from initial detection through full resolution.

> **Note:** You will need the Tines Webhook URL and admin privileges to configure Amazon EventBridge.

### Steps

Once you are ready with the Tines Webhook URL, you will:

1.  Create an EventBridge Connection
    
2.  Create an EventBridge API Destination that uses the Connection
    
3.  Add a rule to route Security Hub Findings to the API Destination
    

### Create an Event Bridge Connection

1.  Navigate to the Amazon Event Bridge admin console, select Connections, and click Create connection
    
    ![250](https://downloads.intercomcdn.com/i/o/am0g137l/1827236957/bed41aaecaeedda1ad56e6e67eb8/f3a1932f-bec9-44f0-a53d-56b5db7b4b83?expires=1784851200&signature=bbaf98986f8f679c32d72471acd5ef5fef9ec7404d005ee96b7519a0888ae190&req=dSglEct9m4haXvMW3nq%2BgYf3MprJIpTsaXxmzEZtpxg4%2FVUzKXI5LPgkTm5e%0A8yK%2Fswi%2F8cm4NsJQLCv%2Bo0ieCfo%3D%0A)
    
2.  Enter a name for your connection and select a public API type
    
3.  For Configure authorization, select Custom configuration, API Key authorization type, enter x-not-used for the API key name, and not-used for the key value
    
    ![700](https://downloads.intercomcdn.com/i/o/am0g137l/1827237519/bec74b601756bfc31822f0ce4faa/f7475628-a2a8-4ee7-bc45-02d74054262e?expires=1784851200&signature=67077f2b811f8aa05913b23f2757495bebe597be1076744153c05eaf2b6209db&req=dSglEct9moReUPMW3nq%2BgfsSNlnwidN6ArNjqQHqH3G5preYuHPHycPTIvMi%0AoApoh94SksVOdSoMEpifVATcmXA%3D%0A)
    
4.  Click Create
    

### Create an EventBridge API Destination

1.  Navigate to the Amazon Event Bridge admin console, select API destinations, and click Create API destination
    
2.  Enter a name for your API destination, paste the Webhook URL in the API destination endpoint field, and select POST for the HTTP method
    
3.  Use an existing connection type and select the connection created in the previous step
    
    ![800](https://downloads.intercomcdn.com/i/o/am0g137l/1827240652/5e523c48225cef366309b301cabf/e90e54cd-2547-4558-bd13-ede37fff2bf3?expires=1784851200&signature=e337544b62227d364f43fe8a048c4178cf4fc0fd14bf8311e41131bedc525dbc&req=dSglEct6nYdaW%2FMW3nq%2BgQqP72Pm%2FZSxAWA2VOgCGD%2FEQc5Cg3Gm531L1U%2BQ%0AAC6uZCOMKGfHPjk9hWdEJVNC1u4%3D%0A)
    
4.  Click Create
    

### Create an EventBridge Rule

1.  Navigate to the Amazon Event Bridge admin console, select Rules, and click Create rule
    
    ![250](https://downloads.intercomcdn.com/i/o/am0g137l/1827242084/c6e8f421c3b531cc98f3356abc59/ae7f5f2c-dc8c-4044-b48d-0ed8be87fc71?expires=1784851200&signature=1445de53ace864e11c1f50e8d36c46570cb79a4f40628fd228b1fd2075b69fef&req=dSglEct6n4FXXfMW3nq%2BgTofV9Q0mpA8VQAOAPFNkaqciPyBIrFOfIjbosEM%0AjZRDlTF66Eb6Xa2xyDkD%2FHW7lxQ%3D%0A)
    
2.  Enter a name and click Next
    
3.  For Events select Other and for Event pattern select Custom pattern (JSON editor)
    
    ![500](https://downloads.intercomcdn.com/i/o/am0g137l/1827243355/7ced669a6e7e000ddfb178dd5dc3/08d62207-ee07-40f9-b51d-5098026248de?expires=1784851200&signature=9aa6de565609ca931ab000ff1c3ecedc72ecadf43494d0f18d6cbaa48487e687&req=dSglEct6noJaXPMW3nq%2BgXuI3L%2FasxIZng6dW%2BTmIjZFGb40%2FW0EhOJIQ2Oi%0ArzBv6eqXGjw1PzmBCcwn2J0HAzE%3D%0A)
    
4.  Paste the following into the JSON editor
    

```
{
 "source":[
  "aws.securityhub"
 ],
 "detail-type":[
  "Findings Imported V2"
 ],
 "detail":{
  "findings":{
   "metadata":{
    "product":{
     "name":["Security Hub Exposure Detection"]
    }
   }
  }
 }
}
```

This rule routes Security Hub exposure findings. Please see [Configuring rules for EventBridge](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-v2-cwe-event-rules.html) for additional detail.

1.  Click Next
    
2.  Select EventBridge API destination, use an existing API destination (the one created earlier), and click Skip to Review and create
    
3.  Scroll to the bottom of the page and click Create rule
    

### Orchestrate and Remediate Security Hub Findings

Security Hub exposure findings will now be sent to the Tines Webhook specified by the API destination. To get started check out the [AWS Security Hub Stories in the Tines Library](https://www.tines.com/library/tools/aws-security-hub/). Note if you change the Tines Webhook URL or add new Webhooks, you will need to update or create new API destinations.

## Connecting to APIs in Tines using credentials

> **For a list of tool-specific authentication guides, [see here](https://explained.tines.com/en/collections/3801629-authentication-credentials).**

* * *

[Watch video](https://fast.wistia.net/embed/iframe/y0wkwcdn2d)

Most APIs require you to prove your identity. Like passwords for your accounts, APIs require a secret token before providing access. Credentials in Tines let you securely store these secret tokens and use them when making requests to any tool that has an API.

Good credential management is critical when working with APIs. Not only do credentials need to be secure, but they also need to be accessible. Tines can store many kinds of credentials like basic text, OAuth, and AWS credentials. One of the most flexible credential types is the HTTP Request action credential (HRAC). This type of credential allows users to dynamically authenticate to APIs that might have multiple legs of their authentication or retrieve passwords from secure privilege access managers.

### Security Considerations when using credentials

It is important to note that while this method dramatically increases the security of secret information in Tines, like any piece of security, it's not absolute.

For example, if Tines is interacting with a 3rd-party service, it is possible the service, depending on its operation, may insecurely include the credential's value in its response, which Tines will then include in an emitted event.

## How to create a Credential

There are two main ways to create a credential in Tines: via your dashboard, or directly from within a story.

### Create a credential via your dashboard

If you know what credential you want to create, you can do so directly from your dashboard.

1.  Select the team you wish to add the credential to.
    
2.  Select "Credentials"
    
3.  Click "New Credential" and select the tool you are connecting to or the type of credential required.
    

![](https://downloads.intercomcdn.com/i/o/840039422/f2a1c8b6b16a3c93c3b8925f/Screenshot+2023-09-27+at+12.00.57.png?expires=1784851200&signature=b6313fdaa8bf9206e6b3877636e44a4e6fc474bc64ad68987f03d33038712e6c&req=fCQnFsp3mYNdFb4V1XW4gRynhsw89bM6P7%2B7lQROZCAww6s%2FFhXhK9PvsuGe%0AnzXvhBcgygg5ZEbGRxbT%2BnZgMA%3D%3D%0A)

1.  Each type of credential requires different information. Complete the required fields. You can find more details of the different types of credentials [here](https://www.tines.com/docs/credentials).
    
2.  The credential is now available for use in the stories in the team.
    

### Create a credential in a story

Open the storyboard and select "Templates"

1.  Select the vendor you wish to authenticate with. In this example, we select Okta.
    
    ![](https://downloads.intercomcdn.com/i/o/841950332/d6988b2b6d865573a0f03de6/Screenshot+2023-09-29+at+14.24.46.png?expires=1784851200&signature=1716eaf36def2668c5a51dec1ab60839714fa95a05998d7bca2d0a9cb3fa5525&req=fCQmH8x%2BnoJdFb4V1XW4gVhX8eaz8sFqGXTnB2cDgeq6Hqpvfxu3xK5O7HLY%0AnJ1nkuvp1MmO%2F4wTQGrnGXbhvw%3D%3D%0A)
    
2.  Drag a template onto the storyboard.
    
    ![](https://downloads.intercomcdn.com/i/o/841950517/36fcece3a1b096aecc2cde63/Screenshot+2023-09-29+at+14.24.58.png?expires=1784851200&signature=5f08f202327e0feb20827dd220d19f09dc682127db026c406e6504701d018a89&req=fCQmH8x%2BmIBYFb4V1XW4gdOsg1hGbWA88hDdO0BIytUzNzYSBdwrCfmf%2Fk3c%0AxGSxeHz%2BsLJLTqrCEbpvVH6Wgw%3D%3D%0A)
    
3.  Click "Connect on the right in the configuration panel.
    
    ![](https://downloads.intercomcdn.com/i/o/841950739/eb7f8cd66f0ef033b6e0de13/Screenshot+2023-09-29+at+14.25.08.png?expires=1784851200&signature=a562eb0f5ed6f61357b505ddbfb8e8647eaaa1a5314923060f7fd8a185c2aed4&req=fCQmH8x%2BmoJWFb4V1XW4gXF10tR5HGCLkSfsVqWDn9Y2OoZfN3ucXeDcgqDU%0Aa2HPg%2FNkGfuEbaOnRZLc7v%2Blyg%3D%3D%0A)
    
4.  Enter the required details in the popup.
    
    ![](https://downloads.intercomcdn.com/i/o/841951102/cc95e27261c2c0be7a635ddb/Screenshot+2023-09-29+at+14.25.47.png?expires=1784851200&signature=1e57a8362f04631c4138e1bc36782883cf7c3f70bd2f40539de62fb3081f5700&req=fCQmH8x%2FnIFdFb4V1XW4gVrennsS7XnrzQO9KL9a1mKi1g3RNLP%2BrDgljsrf%0AIHybahTC6aoq4cWSeUy9OIfKuQ%3D%3D%0A)
    
5.  The credential will now be available for use.
    
    ![](https://downloads.intercomcdn.com/i/o/841951322/0f38e98e302a529a88a6f081/Screenshot+2023-09-29+at+14.26.23.png?expires=1784851200&signature=105ad435723d650156f1604e53af9133f3c09760ca8bd5b2a96d430dcbfd97db&req=fCQmH8x%2FnoNdFb4V1XW4gbRpsHQnL0h8%2FvoQq8IAsgKzLA%2BNe3C76JsHKjYr%0AQ20GZ3VfbSP0mtVulMaM%2BmbRHg%3D%3D%0A)
    

> **Note:** We recommend you control access to your credentials where possible. To learn more about restricting credentials, [see here](https://www.tines.com/docs/credentials#restricting-a-credential).  
> ​
>
> **Note:** Not every tool we connect to has the connect button and flow outlined above (though we continue to add more each week). But that doesn't mean you can't still connect to those tools.

### Sharing a Credential

Credentials will, by default, only be accessible to the team they are created within. Credentials can be configured to be shared with all other teams in the tenant by selecting the 'Everyone' access option.

![](https://downloads.intercomcdn.com/i/o/732535304/bd4b25d0b62fc1c61fb9ada8/image.png?expires=1784851200&signature=71639d5459d75f8d5c4e6ac85d9e20180072285a64227336f2ba97bbd8b426b8&req=cyMlE8p7noFbFb4V1XW4gWAsNfgtXLmRxuCJOAMZCeZER3IsNbWVk6mubZzQ%0ANelqzuAOtSYBqG%2BE9t8GJQCQAg%3D%3D%0A)

Credentials with the same name as Credentials shared across multiple Teams will use the Credential located within the same Team as the Story.

> **👉 For more details on each credential type [see here](https://www.tines.com/docs/credentials).**

## Connecting with the Tines builder community

## Building together: Ways to engage with fellow Tines builders

We’re proud of our builder community. By connecting with other builders, you’ll discover new ways to use Tines, share your expertise, and learn from peers who are solving similar challenges across industries.

This is your space to collaborate, ask questions, and get inspired… whether you’re looking for practical advice, creative workflow ideas, or simply a network of people who get what you do. Together, we’re building smarter, faster, and more intelligent workflows… one story at a time.

In this spirit of collaboration and shared success, we offer multiple avenues for our builders to connect, share insights, and inspire one another.

### Join the Tines Slack Community

Our Slack community serves as the digital hub for Tines builders worldwide. Here, you can:

-   Ask questions and get real-time assistance from other builders
    
-   Share your stories and workflows
    
-   Discover innovative use cases and implementation strategies
    
-   Stay current on product updates and upcoming events
    
-   Network with professionals facing similar challenges
    

The Slack community provides an accessible, always-on space for builders at all skill levels to connect and grow together. Fill out [this Tines page](https://hq.tines.io/pages/6f8b122ccba3cb7e8e0d3531d1b70eb2/) to join today.

### Attend Builder Connect events

Throughout the year, we host [Builder Connect](https://www.tines.com/events/builder-connect/) events in various cities worldwide. These in-person gatherings offer unique opportunities to:

-   Meet local Tines builders face-to-face
    
-   Participate in hands-on workshops and demonstrations
    
-   Share workflow challenges and solutions
    
-   Network with regional security, IT, and operations professionals
    
-   Learn directly from Tines experts and power users
    

Builder Connects blend technical learning with community building, creating spaces where relationships form and collaboration flourishes.

### Meet us at an industry event

Look for the Tines team and community members at major industry events including:

-   BSides conferences
    
-   Black Hat
    
-   ElasticON
    
-   And many other IT and security conferences [here](https://www.tines.com/customer-center/#events)
    

These events provide excellent opportunities to connect with Tines representatives and fellow builders while exploring how Tines intelligent workflows fit into the broader security, automation, and AI ecosystem.

### Participate in "You Did WHAT With Tines?!" (YDWWT)

Our popular [YDWWT](https://www.tines.com/ydwwt) competition showcases the creative and unexpected ways builders are using Tines. By participating, you can:

-   Discover unconventional workflow use cases
    
-   Get inspired by innovative approaches to common challenges
    
-   Share your own unique implementations
    
-   Learn how others are pushing the boundaries of what's possible with Tines
    

YDWWT celebrates the ingenuity of our community and demonstrates the platform's flexibility across diverse scenarios.

### Annual builder recognition

We continue to celebrate builder ingenuity each year by further recognizing outstanding contributions and implementations across our community. This is done through programs such as the [Top Builder](https://www.tines.com/blog/what-does-it-take-to-become-a-top-builder-in-tines/) awards, [YDWWT competitions](https://www.tines.com/ydwwt), webinars, and more.

This recognition:

-   Highlights exceptional stories
    
-   Celebrates innovative problem-solving
    
-   Identifies builders who have achieved remarkable results
    
-   Inspires the broader community with real-world success stories
    

### Advisory Boards

We offer paths to participate in our invitation-only Customer Advisory Board (CAB) and Technical Advisory Boards (TAB). These groups provide strategic input on our storytelling, sharing top-of-mind problems from the industry, and ensuring we continue to evolve in alignment with customer and market needs.

## Our commitment to community

At Tines, we believe that the strength of our platform is magnified by the strength of our community.

Whether you're troubleshooting a specific workflow, seeking inspiration for new use cases, or simply wanting to connect with like-minded professionals, the Tines builder community offers valuable resources and relationships to support your success.

We invite you to engage with fellow builders through any or all of these channels. Let’s continue building together!

## "Could not create event – Story run has reached the maximum number of events in a single branch." Error

## Overview

As you build and run stories in Tines, you might run into the following error:

```
Could not create event – Story run has reached the maximum number of events in a single branch.
```

This article covers the cause and solution to this behavior.

## Cause and solution

This error means the Tines 30k event limit has been reached for a single branch (this is limited to a particular story run). A branch means an individual path in a story run.

For example, in the screenshot below, the story run had 5 events, across 2 branches which each are 3 events long (as the execution splits along 2 paths after the first action). We typically see the error happen when there is an infinite loop involved in a story:

![](https://downloads.intercomcdn.com/i/o/1171486291/3e0d38c6b0aaed56811d1bba/image.png?expires=1784851200&signature=8ca1ae83cab7580916143c73a0b086205c3f5e9e29a00c4ddf7f6d3186a1fc1c&req=dSEgF812m4NWWPMW3nq%2BgQkWmzcH%2F5zra%2BUBSW8QJs7sg4uiF9sdSU9dXdWS%0AS4ALo4jQV3A8K6aOoQZBNW22vFk%3D%0A)

> 🟢 **Solution**: Navigate to the story the alert was created for, identify, and correct where a possible infinite loop might be happening.

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**, when available:

> ℹ️ **General Information:**
>
> -   Date and time of the reported behavior.
>     
>
> ℹ️ **Story Details:**
>
> -   Impacted Story's URL (include all URLs if there are multiple stories impacted).
>     
> -   Impacted Action's ID (include all IDs if there are multiple actions impacted).
>     
> -   The action configuration(s):
>     
>     -   You can copy + paste actions into a code block; this will provide the configuration of the action(s) in a JSON format so that Tines Support can use it into our test environments.

## "Could not find a matching action to return event..." Error

## Overview

While building out and running stories in Tines, especially with [send to story actions](https://www.tines.com/docs/stories/send-to-story/), you might run into the following error on your sub-stories:

```
Could not find a matching action to return event. If this action ran because of a Send to Story event, ensure that the original event contained #agent_id and #event_id keys.
```

This article covers common causes and solutions to the behavior.

## Cause and Solution

### Cause One: Independent action run

An action within a send to story flow was run independently, outside of the send to story flow (i.e. manually running an action).

> 🟢 **Solution:** If this occurred because you ran an action manually, you'll want to re-emit the last event from the upstream webhook "entry" action in the story instead.
>
> ![](https://downloads.intercomcdn.com/i/o/1171582768/94b0344f4af7b9fabbd02b2e/image.png?expires=1784851200&signature=a0297ef9f6bb8834a2cc23a177a3ecd562da9fcf03d87d6e297f9cda97ac922b&req=dSEgF8x2n4ZZUfMW3nq%2BgdU%2FPKIb8w1e2siGixeFCRJaO8LicweyF%2FsGrotj%0AcaUDMSGXgJ741Qvx%2BYawMYLhnzM%3D%0A)

### Cause Two: Page action outside of the send to story flow

There is a page action in the sub-story as another input/entry. When the page action emits an event, this error will show in the exit action of the sub-story, as the flow happened outside of the send to story flow.

> 🟢 **Solution:** Include page actions within your send to story flow.
>
> ![](https://downloads.intercomcdn.com/i/o/1171576021/07816b3ca93965fd48c49935/image.png?expires=1784851200&signature=88e1b7a2b7bc36ad3dbbd08acfccff246dd30d97dd7358aee08829ce13c3ee06&req=dSEgF8x5m4FdWPMW3nq%2BgQ7WxXvdvCOpibt5hx6cAxhinULvUTVXWnU7exAQ%0ACfLUUyNPc0PUMJmQdE2RI9Z4R3c%3D%0A)

### Cause Three: Non-send to story event source

A story is set up for a send to story flow, and a request comes into a webhook (configured as an "entry" action) from a source other than a send to story action.

> 🟢 **Solution:** If you are not using a story in a send to story flow, make sure to turn off the **Send to Story** setting within the story configuration panel.
>
> ![](https://downloads.intercomcdn.com/i/o/1171580122/8fcda4a11b550f4a87121ca3/image.png?expires=1784851200&signature=b638e1e1088bf0cc8e64cd2bd8dfa662b65f62f3cb5bce0a89cfe19dc6f0ec7a&req=dSEgF8x2nYBdW%2FMW3nq%2BgVMDXCId3a3yUzEdFBIpF4pFu8Gx2HGacR5TsHTk%0AoZwKIf%2BeYboH5nxt9Jo0eQ5PmCw%3D%0A)

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**, when available:

> ℹ️ **General Information:**
>
> -   Describe the behavior in detail, including steps to replicate (*we recommend providing this information via a screen recording*).
>     
> -   The time and date of the reported behavior.
>     
>
> ℹ️ **Story Details:**
>
> -   Impacted Story's URL (include all URLs if there are multiple stories impacted).
>     
> -   Impacted Action's ID (include all IDs if there are multiple actions impacted).
>     
> -   Relevant action event data. You can find this via the [event panel](https://www.tines.com/docs/events/).
>     
> -   The action configuration(s):
>     
>     -   You can copy + paste actions into a code block; this will provide the configuration of the action(s) in a JSON format so that Tines Support can use it into our test environments.

## Create and update a record

Records are a powerful feature in Tines used to introduce structured data into your team's stories, which is particularly useful for performing trend analysis to monitor when specific actions or events occur.

The below video demonstrates create a record in Tines.

[Watch video](https://www.youtube.com/embed/3gxechUZTUA?rel=0)

## Creating a record

To capture and store structured data from your story actions, follow these steps:

1\. Add the record action: Go to tools on the left-hand side and drag a record action onto your story canvas.

2\. Connect actions: Connect the action preceding where you want to store the data to the Record action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2219611079/b6f890108a08b6782133c9d17fb3/add%2Brecord.gif?expires=1784851200&signature=9f5cb1535974da4738289327a30e16dc03e0ad4198a98d6440236fcb4b127ffb&req=diImH89%2FnIFYUPMW3nq%2BgaDf0orGR9CmY7LTeOia2NimuCygRBTUsujp9%2BA3%0ARoTPNKHNyRl2xUVLj333RUD42TQ%3D%0A)

3\. Define the record type:

-   In the record action configuration, choose your record type.
    
-   To create a new type, click create new at the bottom.
    
-   Give your record a name, such as "malicious IP".
    

4\. Configure columns: By default, `timestamp` and `story name` are stored. Add any custom values (columns) you need, such as `IP` (as text). You can change the data type of a column from text to a number if necessary.

5\. Click save.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2219615303/e04515b2e0892dcd431f8502c734/record+type.gif?expires=1784851200&signature=d35f4b4e2bef2fab60c9787b5677c08a5782b65073fa1bf0cea2324c5d2b102a&req=diImH89%2FmIJfWvMW3nq%2Bgfnbu3qw0e1pyKz9lb48g38C2V9NS1eyYI2qyEpi%0AVhZAthb1DoPcrB0aNxuoWMO0NJU%3D%0A)

## Setting the value for data storage

Next, set the values for the custom columns using data from previous actions in your story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2219616043/d5d38a4b1def47ec222347f019a0/796c4c58-303c-4d53-a292-fc9fbddfc816?expires=1784851200&signature=7d45c95aa7c9a54979924dfa934a86396e49cbbd4136219f4715330bdc0e6550&req=diImH89%2Fm4FbWvMW3nq%2BgZmALR%2Fr5Mol1V5E2uYslgEUudrMi6ai7cW%2FnGgJ%0AszOvTUcbqL51L0UejM7eyPhL1So%3D%0A)

## Viewing and managing records

Once the event has passed through the record action, the data is stored.

### Accessing records

1.  Go back to your **team** view.
    
2.  Scroll down to **records**.
    
3.  Search for your record type (e.g., "malicious IP").
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2219617252/3f3a4b4baa1d53adb765b2c72f62/records+navigation.gif?expires=1784851200&signature=c7df6df911654b488e5ecc60d2734379b03baf0e58780b66c46d5cb851da8da4&req=diImH89%2FmoNaW%2FMW3nq%2BgbgEKCwjMGwOwkX92w0PAA1OiClFliaPao7AqpR6%0AUDBhCo%2F1%2F2xq9ILhmV1OeXHU58Q%3D%0A)

The records view displays all associated columns: story name, timestamp, IP, and scan.

### Filtering and reporting

You can manage the stored data directly from the records view:

-   Filter: In the top right-hand corner, you can filter your data by date, choosing specific dates or ranges like yesterday, the last seven days, or the last 365 days.
    
-   Customize view: You can remove the view for certain columns.
    
-   Export: Export the data as a CSV file to create reports for leadership.
    

### Reviewing a single record

To view the data associated with a single event:

-   Click the arrow on the left-hand side of a record entry.
    
-   This pulls up the data and payload from the story that generated the record.
    
-   From this detailed view, you can click a link to view the story it originated from or copy the link to the record to share internally.
    

## Updating a record

An existing record can be updated via:

-   Tines Tenant
    
-   Tines API
    

### Tines Tenant

Enabling the ‘Set as editable’ option will allow you to edit the values within your record.

> Note: The values within the ‘Story Name’ and ‘Timestamp’ record columns cannot be edited.

Within your tenant, navigate to the record > Manage record and enable ‘Set as editable’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2222240323/d0a85231eed7adc1c326e910936c/set+record+as+editable.gif?expires=1784851200&signature=cc91425c56c4192c034a94fffd135f666333a80749c7314564b672209ca19887&req=diIlFMt6nYJdWvMW3nq%2BgZh%2FXJW8pO7HH1C0x%2BmnJBh%2Fe9qTyK4Hc5HmmFJ%2B%0AJhpF%2FLxiLWYLXQzso8MIoswpSQ8%3D%0A)

### Tines API

Use the [update](https://www.tines.com/api/records/update) records Tines API to update records.

> Read more about records in our docs [here](https://www.tines.com/docs/records/).

## Creating credentials in Tines

You can create a credential at the team level in Tines or within the story. A credential will only be available in the story when it is created for the team in which that story resides.

> **To learn more about credentials, see [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**

[Watch video](https://fast.wistia.net/embed/iframe/y0wkwcdn2d)

### Create a credential for the team

1.  Select the team you wish to add the credential to.
    
2.  Select "Credentials".
    
3.  Click "New Credential" and select the tool you are connecting to or the type of credential required.
    
    ![](https://downloads.intercomcdn.com/i/o/840039422/f2a1c8b6b16a3c93c3b8925f/Screenshot+2023-09-27+at+12.00.57.png?expires=1784851200&signature=b6313fdaa8bf9206e6b3877636e44a4e6fc474bc64ad68987f03d33038712e6c&req=fCQnFsp3mYNdFb4V1XW4gRynhsw89bM6P7%2B7lQROZCAww6s%2FFhXhK9PvsuGe%0AnzXvhBcgygg5ZEbGRxbT%2BnZgMA%3D%3D%0A)
    
4.  Each type of credential requires different information. Complete the required fields. You can find more details of the different types of credentials [here](https://www.tines.com/docs/credentials).
    
5.  The credential is now available for use in the stories in the team.
    

### Create a credential in a story

> **Not every vendor has this capability. We continue to add more each week.**
>
> **To access authentication guides for various tools, [see here](https://explained.tines.com/en/collections/3801629-authentication-guides).**

1.  Open the storyboard and select "Templates".
    
2.  Select the vendor you wish to authenticate with. In this example, we select Okta.
    
    ![](https://downloads.intercomcdn.com/i/o/841950332/d6988b2b6d865573a0f03de6/Screenshot+2023-09-29+at+14.24.46.png?expires=1784851200&signature=1716eaf36def2668c5a51dec1ab60839714fa95a05998d7bca2d0a9cb3fa5525&req=fCQmH8x%2BnoJdFb4V1XW4gVhX8eaz8sFqGXTnB2cDgeq6Hqpvfxu3xK5O7HLY%0AnJ1nkuvp1MmO%2F4wTQGrnGXbhvw%3D%3D%0A)
    
3.  Drag a template onto the storyboard.
    
    ![](https://downloads.intercomcdn.com/i/o/841950517/36fcece3a1b096aecc2cde63/Screenshot+2023-09-29+at+14.24.58.png?expires=1784851200&signature=5f08f202327e0feb20827dd220d19f09dc682127db026c406e6504701d018a89&req=fCQmH8x%2BmIBYFb4V1XW4gdOsg1hGbWA88hDdO0BIytUzNzYSBdwrCfmf%2Fk3c%0AxGSxeHz%2BsLJLTqrCEbpvVH6Wgw%3D%3D%0A)
    
4.  Click "Connect on the right in the configuration panel.
    
    ![](https://downloads.intercomcdn.com/i/o/841950739/eb7f8cd66f0ef033b6e0de13/Screenshot+2023-09-29+at+14.25.08.png?expires=1784851200&signature=a562eb0f5ed6f61357b505ddbfb8e8647eaaa1a5314923060f7fd8a185c2aed4&req=fCQmH8x%2BmoJWFb4V1XW4gXF10tR5HGCLkSfsVqWDn9Y2OoZfN3ucXeDcgqDU%0Aa2HPg%2FNkGfuEbaOnRZLc7v%2Blyg%3D%3D%0A)
    
5.  Enter the required details in the popup.
    
    ![](https://downloads.intercomcdn.com/i/o/841951102/cc95e27261c2c0be7a635ddb/Screenshot+2023-09-29+at+14.25.47.png?expires=1784851200&signature=1e57a8362f04631c4138e1bc36782883cf7c3f70bd2f40539de62fb3081f5700&req=fCQmH8x%2FnIFdFb4V1XW4gVrennsS7XnrzQO9KL9a1mKi1g3RNLP%2BrDgljsrf%0AIHybahTC6aoq4cWSeUy9OIfKuQ%3D%3D%0A)
    
6.  The credential will now be available for use.
    
    ![](https://downloads.intercomcdn.com/i/o/841951322/0f38e98e302a529a88a6f081/Screenshot+2023-09-29+at+14.26.23.png?expires=1784851200&signature=105ad435723d650156f1604e53af9133f3c09760ca8bd5b2a96d430dcbfd97db&req=fCQmH8x%2FnoNdFb4V1XW4gbRpsHQnL0h8%2FvoQq8IAsgKzLA%2BNe3C76JsHKjYr%0AQ20GZ3VfbSP0mtVulMaM%2BmbRHg%3D%3D%0A)
    

> **We recommend you control access to your credentials where possible.**
>
> **To learn more about restricting credentials, [see here](https://www.tines.com/docs/credentials#restricting-a-credential).**

## Credentials FAQs

## How are credentials stored in Tines?

Credentials that are configured within Tines to authenticate with other systems are stored in two ways:

1.  For cloud Tines tenants, the AWS RDS database.
    
2.  For self-hosted Tines tenants, in the Postgres database.
    

## If a resource or credential is renamed, does it update the change to all stories?

Yes, any story using a resource or a credential that is renamed that change will be updated in those stories.

## Is there a limit on the number of credentials on a tenant?

No, there is currently no limit on the number of credentials that can be stored in Tines.

## Can I have more than one credential in a story?

Yes, you can have more than one credential in a Tines story.

Tines can interact with several systems in one story, for example, taking information from Slack or MS Teams and updating a Jira or ServiceNow ticket.

> Note: Some systems may only allow one credential or token when interacting with them, e.g. Slack

## Why is my credential not showing on the storyboard?

A credential will only appear on the properties panel when used in an action.

## Can I have multiple Slack credentials within Tines for the same Slack workspace?

No. If there is more than one active Slack credential for the same workspace, Slack will revoke one token when the other token refreshes. This applies to the entire Slack tenant.

> For more detailed information on credentials, read our docs [here](https://www.tines.com/docs/credentials/).

## Credentials in Tines

Credentials in Tines are a secure way to store sensitive information such as API tokens and passwords that need to be used in actions. The `credential` formula key allows users to reference this sensitive information without exposing the actual values in action configurations.

Think of credentials in Tines as your digital vault—a secure, centralized place to store sensitive information like API keys, passwords, and tokens that your automation needs to access critical systems.

### **Types of credentials**

Tines supports several types of credentials:

-   Text
    
-   JWT tokens
    
-   OAuth2.0
    
-   AWS
    
-   HTTP Request
    
-   Mutual TLS
    
-   Multi Request
    
-   Product-specific credential flows
    

### **Creating credentials**

Credentials can be created in two ways:

1.  From the dashboard by selecting your team name, then "Credentials," and clicking "New"
    
2.  Directly within a story using templates for specific vendors
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1807181800/451ff73a518233bfc284a84ab08a/CleanShot%2B2025-10-31%2Bat%2B18_04_14-402x.png?expires=1784851200&signature=c52d561c3057ba3dbd2ecf633376148ba7888b85777d790979b807ef6471ec8b&req=dSgnEch2nIlfWfMW3nq%2BgXPrfcbpazuTdlyXWtlmlh%2BiujVz7Va3SUjtermr%0A3NNbDdxCTHLJAgDh6RoRvUYvFs8%3D%0A)
    

Watch how to create a credential here:

[Watch video](https://fast.wistia.net/embed/iframe/y0wkwcdn2d)

> **👉 Learn more about credentials [here](https://www.tines.com/docs/credentials/).**

## Cribl Authentication Guide

### First, get credentials from Cribl

1.  Login to [Cribl Cloud](https://login.cribl.cloud/)
    
2.  On the top bar, select **Products**, and then select **Cribl**.
    
3.  In the sidebar, expand **Organization**, and then select **API Credentials**.
    
4.  Select **Add Credential** (or Add an API credential, if this is the first Credential in the system).
    
5.  Specify a **Name** and an optional Description of the Credential’s purpose.
    
6.  If applicable, under **Organization Permissions**, select the permission that the Credential’s tokens will grant.
    
7.  If you chose the User permission, under Workspace Access, define access to specific Workspaces.
    
8.  If you selected the Member permission for any Workspace, additionally select product-level permissions for that Workspace. (The User permission is not available for API Credentials.)
    
9.  Confirm with Save.
    
10.  The new API Credential will appear on the **API Credentials** page. Each Credential exposes a Client ID and Secret, details of Workspace access and other information.
     

### Then, get your Cribl Cloud URL

To locate your Cribl.Cloud URL, navigate to **Workspace** > **Access** in the sidebar of your Cribl.Cloud.

### Finally, create a Cribl credential in Tines

**Cribl connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Cribl" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/cribl).**
>
> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Cribl stories in the [story library](https://www.tines.com/library?view=all&s=cribl).**

## Criminal IP Authentication Guide

**Criminal IP** is a Cyber Search Engine and Attack Surface Management(ASM) platform to find everything in Cybersecurity with an impressive amount of data capacities, API speed, and price.

## First, get your CriminalIP API Key

1.  Login to your [CriminalIP](criminalip.io/en/login?h2=%2F) account. New users can "Register" for a Criminal IP account if they do not already have one.
    
2.  On the main page, click your "Profile" and then "My Information"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/767237133/81c03069540e44f13a8e2912/image.png?expires=1784851200&signature=72800a195bf2100d0ee26fcb7c01c52de0538312bdc94d5f5fe1ec47d8daaf12&req=cyYgFMp5nIJcFb4V1XW4gZ53i8mXxjtPtF9%2FiX71KuVPEilPI8DIk6GVtK9k%0AabhTXGoB17bVa4WkLK67d9pMAA%3D%3D%0A)
    
3.  Under "API Key", click the "Copy" button  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/767237467/2dfa23155e265c53320c070a/image.png?expires=1784851200&signature=1535ec336c5d603a456aca7b35fe70214708f6af67cf0c98341c055bbf82f792&req=cyYgFMp5mYdYFb4V1XW4gfsxOZhx3AuZw2Ytrf0NSBG%2FF6hU%2Fn%2F4yp%2BheFzw%0AFzKRArBIT0HeONjMuEfK7StLLg%3D%3D%0A)
    

##   
  
Then, create a CriminalIP credential in Tines

**CriminalIP connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "CriminalIP" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the CriminalIP connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the CriminalIP credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    4.  **Domains:** api.criminalip.io
        
    5.  **Access:** What other teams can also use the API  
        ​
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/criminal-ip).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of CriminalIP stories in the Tines [story library](https://www.tines.com/story-library?s=criminalip)**[.](https://www.tines.com/story-library?s=criminalip)

## CrowdStrike Authentication Guide

CrowdStrike is a global cybersecurity leader with an advanced cloud-native platform for protecting endpoints, cloud workloads, identities, and data. Here's our guide for integrating with CrowdStrike.

## First, create a CrowdStrike API client:

1.  Go to [https://www.crowdstrike.com/login/](https://www.crowdstrike.com/login/)
    
2.  In your CrowdStrike account, visit "Support and Resources" and click "API Clients and Keys"
    
    ![](https://downloads.intercomcdn.com/i/o/863738274/7ccc2d1d69154d2d59ef847b/crowdstrike_panel.png?expires=1784851200&signature=ca52537b0f20da3fc333ea69f7afe7608a52897f0d8e9f36d1ca406074e9e4fa&req=fCYkEcp2n4ZbFb4V1XW4gQpAQhhWyozXwLqBq5yKq%2B9mawiw4Tnl5AHLg0pP%0AkMmkdDzZ1yP3EphCVzAurc%2F76Q%3D%3D%0A)
    
3.  Next, select "Create API Client"
    
    ![](https://downloads.intercomcdn.com/i/o/863739094/60e0a09a0c8387b9a175224b/Crowdstrike+create+API+client.png?expires=1784851200&signature=ecea38b268863bb05c60cf3cef7f25c0591fe7cca602e4c70f7d103295b07614&req=fCYkEcp3nYhbFb4V1XW4gbXOdzfNNgloGcA9HByvyWfUAYc720c2SE%2F1s3fT%0AshhgGwlJY4SG5m%2F10jLvA%2FJNPQ%3D%3D%0A)
    
4.  Enter details about the API Client and select the relevant scopes, then click "Create"
    
    ![](https://downloads.intercomcdn.com/i/o/863753349/271e3f5e43c93b0950d06c61/Crowdstrike+API+Client.png?expires=1784851200&signature=6945a4939113978ec36e9ce8498b354b05d7740d1a8074fef0f9e25879b567f4&req=fCYkEcx9noVWFb4V1XW4gYlC7F4JCahBmdY7LUO%2BMsQm5bjE7aJ9Knou0wi9%0ASx1M8Uos4uAaiBB%2F6GaEr%2Fjj3Q%3D%3D%0A)
    
5.  Copy the API "Client ID", "Secret", and "Base URL"
    
    ![](https://downloads.intercomcdn.com/i/o/863754277/97fb124f3ec6b513057b0591/Crowdstrike+API+Secret.png?expires=1784851200&signature=a3d5a929c65e6738b5673f805169d30eb1af156d2a5dc2f251e7391f6ad51ac2&req=fCYkEcx6n4ZYFb4V1XW4gcOjgm3eVSSpT2287aFHp%2BmiPYi9SR4eKmNHgEd9%0AXiyQBEWKs10MGfOvACSWPPiX9Q%3D%3D%0A)
    

## Then, create a CrowdStrike credential in Tines

**CrowdStrike connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team using the CrowdStrike API and click "Credentials"
    
3.  Click "**\+** New Credential" and select "Crowdstrike" from the menu
    
4.  Input the values for the CrowdStrike credential:
    
    1.  **Client ID:** Your Client ID
        
    2.  **Client Secret:** Your Secret
        
    3.  **Base URL:** Your Base URL
        
5.  Click "Connect"
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/crowdstrike).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).  
> You can find a selection of CrowdStrike stories in the Tines [story library.](https://www.tines.com/story-library?s=crowdstrike)**

## Custom certificate authority

### **What is a certificate authority (CA)?**

CA is a trusted entity that issues Secure Sockets Layer (SSL) certificates. This is a common security practice that establishes trust and verify information between systems communicating over the internet.

### **Configuring a *custom* certificate authority in Tines**

Here's how to configure your CA to use across all IMAP and HTTP Request Actions in Tines:

1.  Open your Tines tenant
    
2.  Click the URL bar and input the following at the end of the URL `/admin/custom_ca`  
    It will look like: `https://tenant.tines.com/admin/custom_ca`
    
3.  Enter the PEM encoded X.509 public certificate (or certificate chain) for your custom certificate authority
    
4.  Click `Save`
    

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.

## Custom Tenant URLs

## Overview

Tines supports custom tenant URLs, allowing you to change from the default format like `silent-beach-3471.tines.com` to a custom domain like `acme.tines.com`.

Should you choose to do this, this guide outlines what you will need to be aware of, how to prepare, and best practices for a smooth transition.

> Notes:
>
> 1.  This is only for Business and Enterprise edition tenants
>     
> 2.  To change your tenant name or request a custom domain, reach out to your CSM, CSE or Tines Support, and they will go through the process of setting one up with you.

## Important to know:

-   Webhook URLs - External systems sending webhooks will fail
    
-   OAuth Credentials - Callback URLs will need immediate updates
    
-   SSO Configuration - This may break; please disable before migration OR create recovery codes
    
-   Published Pages - All shared page links will break
    
-   API Endpoints - Scripts/integrations using Tines API will fail
    
-   Email Actions - Stories with hardcoded tenant URLs in emails
    

Both [disabling SSO](https://www.tines.com/docs/admin/single-sign-on/) and [creating login recovery codes](https://www.tines.com/docs/admin/recovery-codes/) are done in Tines.

## Backwards Compatibility

There's an option to keep your old domain temporarily active. This will allow webhooks and pages to keep working with your old URLs.

However, your SSO and OAuth will still require updates.

We recommend fully migrating within 30-60 days of your new tenant URL being deployed

## Pre-Migration Checklist:

We recommend following these suggested steps for a successful migration.

### 1 week before:

-   Audit all systems sending webhooks to Tines
    
-   Document published pages shared externally
    
-   Identify scripts/tools using Tines API
    
-   Search stories for hardcoded URLs
    
-   List all OAuth credentials
    
-   Notify users of migration timing
    

### 3 days before:

-   Decide on your SSO approach (disable or create recovery codes)
    
-   Plan out your OAuth callback updates
    
-   Schedule during low-activity period
    

### Migration Day:

1.  Disable SSO or create recovery codes
    
2.  Execute domain change + enable backwards compatibility
    
3.  Immediately update OAuth callbacks and test
    
4.  Update SSO config and re-enable
    
5.  Test critical workflows
    

> Steps on disabling and enabling SSO can be found [here](https://explained.tines.com/en/articles/12729713-single-sign-on-sso).
>
> Steps on changing your domain name can be found [here](https://www.tines.com/docs/admin/custom-domains/).
>
> Read more about OAuth [here](https://www.tines.com/docs/credentials/oauth/).

### Next steps:

-   Update external webhook senders
    
-   Redistribute new page URLs
    
-   Update API integrations
    
-   Monitor errors daily
    

## Key Tips

### Do:

-   Enable backwards compatibility for transition
    
-   Migrate during off-hours
    
-   Test SSO/OAuth immediately after
    
-   Allow 30 days for full migration
    

### Don't:

-   Disable backwards compatibility until everything is updated
    
-   Skip user communication
    

## Need help

If something isn’t working or you need help with any of the above steps, please contact [support](https://www.tines.com/contact/#support).

## CyberArk Authentication Guide

CyberArk allows you to seamlessly secure identities while accessing any resource across any infrastructure, including hybrid, SaaS and multi-cloud.

You will need the following resources to setup CyberArk Password Vault authentication:

-   CyberArk Password Vault URL
    
-   CyberArk Password Vault Username (for On-Premise)/CyberArk Password Vault Client ID (for SaaS)
    
-   CyberArk Password Vault Password (for On-Premise)/CyberArk Password Vault Client Secret (for SaaS)
    
-   CyberArk Password Vault Account ID (the ID of the specific service/application you need to retrieve credentials for)
    

## Create CyberArk Password Vault resources and credentials in Tines

You will need to create:

-   Three resources: "CyberArk Password Vault URL", "CyberArk Password Vault Username"(for On-Premise)/"CyberArk Password Vault Client ID"(for SaaS), and "CyberArk Password Vault Account ID"
    
-   Two HTTP Request type credentials: "CyberArk Password Vault Token" and "CyberArk Password Vault <Service> Token"
    

#### Resources

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the CyberArk Password Vault resource
    
    1.  **Name:** CyberArk Password Vault URL
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your CyberArk Password Vault URL
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    
7.  Click "+ New Resource"
    
8.  Input the values for the CyberArk Password Vault resource
    
    1.  **Name:** CyberArk Password Vault Username (for On-Premise)/CyberArk Password Vault Client ID (for SaaS)
        
    2.  **Description:** Optional
        
    3.  **Builder:** CyberArk Password Vault Username (for On-Premise)/CyberArk Password Vault Client ID (for SaaS)
        
9.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
10.  Click "Save resource"
     
11.  Click "+ New Resource"
     
12.  Input the values for the CyberArk Password Vault resource
     
     1.  **Name:** CyberArk Password Vault Account ID
         
     2.  **Description:** Optional
         
     3.  **Builder:** Your CyberArk Password Vault Account ID
         
13.  **Optional**
     
     1.  **Access:** What other teams can also use the resource
         
14.  Click "Save resource"
     

#### Credentials

##### Step 1

To generate a CyberArk Password Vault bearer token, follow the steps outlined for the appropriate scenario.

##### Scenario 1: Tines Cloud Hosted and SaaS CyberArk Password Vault

![](https://downloads.intercomcdn.com/i/o/994393365/b6db05272a8f9f89cc44d5cb/image.png?expires=1784851200&signature=d4f69c1bf60cec8e49aad4a572e63b8cbf78f08bc43c4a470bee9b5f12e7bef8&req=fSkjFcB9nodaFb4V1XW4gTfaFs6hfECNboBU8P%2BCihnT%2FpJYYe11Q6yVU3lz%0AXnrf5XVPPFH7cZ5GfUyiW14pkw%3D%3D%0A)

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "HTTP request"
    
3.  Input the values for the CyberArk Password Vault credential
    
    1.  **Name:** CyberArk Password Vault Token
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.cyberark_password_vault_url>>/oauth2/platformtoken`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:  
        ​
        
        ```
        { 
        "client_id": "<<RESOURCE.cyberark_password_client_id>>", 
        "client_secret": "<<secret>>", 
        "grant_type": "client_credentials", 
        "concurrentSession": "True" 
        } 
        ```
        
    7.  **Secret:** CyberArk Password Vault Client Secret
        
4.  Click "Save"
    
5.  Click "Run request" and make sure it returns a successful response.
    
6.  **Location of token from response:** `cyberark_password_vault_token.body`
    
7.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
8.  Click "Save"
    

##### Scenario 2: Tines Cloud hosted and On-Premise CyberArk Password Vault

![](https://downloads.intercomcdn.com/i/o/927114418/c6fbaeddac184192053f2f67/image.png?expires=1784851200&signature=a8731f96d236023b2c99b1c10e9e096f4a900454b4dd639c7e2c2ddcffa88eba&req=fSIgF8h6mYBXFb4V1XW4gTo2UhTk4%2FOr1bwS66AKZ4po%2Fy2u6IsGUAZcZUqa%0A4aG%2BYHIbMSJ0rCPfeK6eV4BHIA%3D%3D%0A)

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "HTTP request"
    
3.  Input the values for the CyberArk Password Vault credential
    
    1.  **Name:** CyberArk Password Vault Token
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.cyberark_password_vault_url>>/passwordvault/api/Auth/cyberark/logon`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:  
        ​
        
        ```
        {
          "username": "<<RESOURCE.cyberark_password_vault_username>>",
          "password": "<<secret>>",
          "concurrentSession": "true"
        }
        ```
        
    7.  **Secret:** CyberArk Password Vault Password
        
    8.  Click "+ Add option" and add "Use tunnel"
        
4.  Click "Save"
    
5.  Click "Run request" and make sure it returns a successful response.
    
6.  **Location of token from response:** `cyberark_password_vault_token.body`
    
7.  **TTL:** `30`
    
8.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
9.  Click "Save"
    

##### Scenario 3: Tines Self Hosted and On-Premise CyberArk Password Vault

![](https://downloads.intercomcdn.com/i/o/927115365/b6b42198bb97cc2941493a33/image.png?expires=1784851200&signature=bf36b56d4439565ec5f670bcc61c5411acb6c62e71ad5ca9e148f3d91b44b6d6&req=fSIgF8h7nodaFb4V1XW4gWb3JytM1fFQES7zAHBCjS9aXF%2FT3GnuOJv6Xbv7%0Ak%2BxPSO1uWJqmWD0MH9QlsVqqHw%3D%3D%0A)

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "HTTP request"
    
3.  Input the values for the CyberArk Password Vault credential
    
    1.  **Name:** CyberArk Password Vault Token
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.cyberark_password_vault_url>>/passwordvault/api/Auth/cyberark/logon`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:  
        ​
        
        ```
        {
          "username": "<<RESOURCE.cyberark_password_vault_username>>",
          "password": "<<secret>>",
          "concurrentSession": "true"
        }
        ```
        
    7.  **Secret:** CyberArk Password Vault Password
        
4.  Click "Save"
    
5.  Click "Run request" and make sure it returns a successful response.
    
6.  **Location of token from response:** `cyberark_password_vault_token.body`
    
7.  **TTL:** `30`
    
8.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
9.  Click "Save"
    

##### Step 2

Now that the CyberArk Password Vault bearer token is stored in a credential, the token can be leveraged to make a subsequent call to retrieve specific service/application credentials.

**For Service/Application token (SaaS):**

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "HTTP request"
    
3.  Input the values for the CyberArk Password Vault credential
    
    1.  **Name:** CyberArk Password Vault <Service> Token
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.cyberark_password_vault_url>>.priviledecloud.cyberark.cloud/PasswordVault/API/Accounts/<<RESOURCE.cyberark_password_vault_account_id>>/Password/Retrieve`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section and modify as required:  
        ​
        
        ```
        {
          "reason": "EnterReason"
        }
        ```
        
    7.  **Headers:** `"Authorization": "<<CREDENTIAL.cyberark_password_vault_token>>"`
        
4.  Click "Save"
    
5.  Click "Run request" and make sure it returns a successful response.
    
6.  **Location of token from response:** `cyberark_password_vault_<service>_token.body` where `<service>` is the service you are retrieving the credentials for
    
7.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
8.  Click "Save"
    

**For Service/Application token (On-Premise):**

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "HTTP request"
    
3.  Input the values for the CyberArk Password Vault credential
    
    1.  **Name:** CyberArk Password Vault <Service> Token
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.cyberark_password_vault_url>>/passwordvault/api/accounts/<<RESOURCE.cyberark_password_vault_account_id>>/password/retrieve`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section and modify as required:  
        ​
        
        ```
        {
          "reason": "api testing",
          "TicketingSystem": "SIR12342134",
          "TicketId": "CHG0000123",
          "ActionType": "show",
          "isUse": false,
          "Machine": "tines"
        }
        ```
        
    7.  **Headers:** `"Authorization": "<<CREDENTIAL.cyberark_password_vault_token>>"`
        
    8.  (Optional) Click "+ Add option" and add "Use tunnel"
        
4.  Click "Save"
    
5.  Click "Run request" and make sure it returns a successful response.
    
6.  **Location of token from response:** `cyberark_password_vault_<service>_token.body` where `<service>` is the service you are retrieving the credentials for
    
7.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
8.  Click "Save"
    

Your credentials should look like the following:

**Step 1**

**For Scenario 1:**

![](https://downloads.intercomcdn.com/i/o/994403783/54d1e68e93509aeb64e3197b/image.png?expires=1784851200&signature=cec1e5e43f724155b30601974ab9f1b777f16f03d548bd1f9c478a91bd4ebd10&req=fSkjEsl9molcFb4V1XW4gchd4PPYw3LjeX%2BQXmp%2BifydAn92Nu3mVffzoUAv%0A5zHVFWAlXHqbLx%2FNo4%2BYU1ypmg%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/994403968/5e4861bf7ac4c7d8f132e45a/image.png?expires=1784851200&signature=4352172d37f36fca74d18de876289d32c2bece18c7ddbcfc7d774074f04f3145&req=fSkjEsl9lIdXFb4V1XW4gfQ%2B0zkE4yd6R82t3%2B%2B88dqX5KeeoypkTFx98%2Fhs%0AzEPonyruo6IZ6DwsHiy%2FxmqRQA%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/994404076/8c8bf2120caf48f2bd41d072/image.png?expires=1784851200&signature=362464be6f086ab9c1e6dfb649b205f80259af00e98880eeaeccede7d1c8c5e9&req=fSkjEsl6nYZZFb4V1XW4gbPTyPkgf9Ef%2FnFLNYntjbiQ0%2Bz3RLk19s7fiTTn%0AwLWHqy1Gir9c1UfemJ8E0I3fsw%3D%3D%0A)

**For Scenario 2:**

![](https://downloads.intercomcdn.com/i/o/927113043/934e988aa8130bc600c83e39/image.png?expires=1784851200&signature=f9955403ae36675152f268d34966699e49a832b453d26dcd0a9e792bcfe2587e&req=fSIgF8h9nYVcFb4V1XW4galhDCAAj2M21XmTuNnYtXW2ck9xDfMl7qeYP2Pp%0AqN7uaVYWe6iwcWBtaBM6zKeMCg%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927113716/04dcfe8ab784c9e7d8394b37/image.png?expires=1784851200&signature=64d67c6dbf4910a5b304fb9884b52f2cb837f9a1500404f7e7ab68fb7e0ad078&req=fSIgF8h9moBZFb4V1XW4gUAij5g2XmQRJZZPNIvlgMJzage%2FnZCqM4J7kxa0%0A2BwUldXIDes%2Fb2XSS8WpGPnXEw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927113774/ed339591de0d858779bae19e/image.png?expires=1784851200&signature=cd2c552ce501237a6fd18f75aa36be5a1e4bdf051876c83031b3f34eadcf31e8&req=fSIgF8h9moZbFb4V1XW4gb736Thqlgw2%2BYfraPm9lcUXvmUlfEwBMCWogohN%0ALQB82MbX0HtGvQ4tu1Zg6uqy4Q%3D%3D%0A)

**For Scenario 3:**

![](https://downloads.intercomcdn.com/i/o/927113043/934e988aa8130bc600c83e39/image.png?expires=1784851200&signature=f9955403ae36675152f268d34966699e49a832b453d26dcd0a9e792bcfe2587e&req=fSIgF8h9nYVcFb4V1XW4galhDCAAj2M21XmTuNnYtXW2ck9xDfMl7qeYP2Pp%0AqN7uaVYWe6iwcWBtaBM6zKeMCg%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927113112/58f88aae30c2039c81e88c08/image.png?expires=1784851200&signature=a5f74c526939a9bb2972a46e7530d89dcb1c511d4bc98542d400a5c15d0ba32c&req=fSIgF8h9nIBdFb4V1XW4gRl1KmgeuLQO3Bb733sss%2Bh%2FNzNWPnETKh9hP8ct%0AG0hoyZUGHnd881X04SQrUMVghQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927113179/c8a0c74b558ec633dfa0e20b/image.png?expires=1784851200&signature=da117415f8029f26320432d8f5adefd3c3766209458baf705c80d7703b9e6f3d&req=fSIgF8h9nIZWFb4V1XW4gf2VMZMgsA0iBuXi02N0ZTJE5sd0NMakKUMpBL%2FS%0ArH0a7B5uw85wmC8lq2jj71VaEg%3D%3D%0A)

**Step 2**

**For Service/Application token (SaaS):**

![](https://downloads.intercomcdn.com/i/o/994405949/defcdb5d4c3545cceeb91da1/image.png?expires=1784851200&signature=576193407a46f830bc951b5db2cbe1633ae8d94e6410a4a9f1084864e7b466d9&req=fSkjEsl7lIVWFb4V1XW4gTopGmeODuafDrRBakogS%2FOjuTlajHHJ5Qmo9%2FHG%0AHDssVLIo1mWSErB%2Ftxa3kAt5zQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/994406631/e4050daa26910f2484124b8f/image.png?expires=1784851200&signature=4930d92c35bd6c2c17dc959b1333398b64115eb70dff3d63825672d4af6055d0&req=fSkjEsl4m4JeFb4V1XW4gZdCeQEO9gdZyE7zyo668juSwoZiTjHO3JiBdUXT%0AKGL5UhTpf5aTe0N7wnIq7YCIPg%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927127948/7114d3878c64cbd587025f22/image.png?expires=1784851200&signature=519ac1348c45e02980d1929be3563438a28d8ffd43e25ce3782e451fe65ddc26&req=fSIgF8t5lIVXFb4V1XW4gQIFgmOLiGJRZ8D5c0DVjeYSruSK7oF1Sok8Kf4B%0ASMbnEdqQ2T2d31Ne2u5chRAxFw%3D%3D%0A)

**For Service/Application token (On-Premise):**

![](https://downloads.intercomcdn.com/i/o/927127817/ca37740d2f20119a6bd9e9a5/image.png?expires=1784851200&signature=88607814a514cfc0bf71bade3672cc1a14492494bdcf889067afd21c4476a280&req=fSIgF8t5lYBYFb4V1XW4gTxXIF4ArDmR4zWOfkCC9zatvKeNH8niaecF%2Bq1L%0ADs2Fh%2BxQRq5nlp6CwvJoRw9EpA%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927127865/aa6026ea210ccf3747e3e63b/image.png?expires=1784851200&signature=75c071a82b211ee102208e75f29d39ab1d15d3c54f97b68f0d40893d1a1225a8&req=fSIgF8t5lYdaFb4V1XW4gRmoMQd6yh9Lvfu6VaApbbApqCwb4KtCx2nGDUr2%0A8Ypkttq6KnmHuM1XiMdsX3H%2BBQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/927127948/7114d3878c64cbd587025f22/image.png?expires=1784851200&signature=519ac1348c45e02980d1929be3563438a28d8ffd43e25ce3782e451fe65ddc26&req=fSIgF8t5lIVXFb4V1XW4gQIFgmOLiGJRZ8D5c0DVjeYSruSK7oF1Sok8Kf4B%0ASMbnEdqQ2T2d31Ne2u5chRAxFw%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of CyberArk Password Vault stories in the [story library](https://www.tines.com/library?view=all&s=cyberark%20password%20vault).**

## Cybereason Authentication Guide

Cybereason offers an endpoint protection platform. It delivers antivirus software, endpoint detection and response with one agent, and a suite of managed services.

### First, create a Cybereason resource in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Cybereason resource
    
    1.  **Name:** cybereason\_username
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your Cybereason username
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

### Lastly, create Cybereason credentials in Tines

You will need to create two credentials:

-   One Text type: "cybereason\_password"
    
-   One HTTP request type: "cybereason\_cookie"
    

##### Text type

1.  Navigate to the team that will be using the API and click "Credential"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the Cybereason credential
    
    1.  **Name:** cybereason\_password
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Cybereason password
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the Cybereason credential
    
    1.  **Name:** cybereason\_ccokie
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://integration.cybereason.net:8443/login.html`
        
    4.  **Content-Type:** `"Custom": "application/x-www-form-urlencoded"`
        
    5.  **Method:** post
        
    6.  **Payload**: Copy the below object and paste into the "Plain code" section:  
        ​
        
        ```
        {
        "username": "<<RESOURCE.cybereason_username>>",
        "password": "<<CREDENTIAL.cybereason_password>>"
        }
        ```
        
    7.  **Disable redirect follow:** Click the "+ Option" button, select choose "Disable redirect follow" and ensure the box remains ticked:  
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/832725944/57a47844ef996222f4204fa1/image.png?expires=1784851200&signature=0b9ad0c12e56974f256a63157cc340136e5aec0a2345077e4408cbdc90fb6c9d&req=fCMlEct7lIVbFb4V1XW4gciVerxTMi979UTFpXBmQCLbe%2B%2BFjbwOT3%2FkkHY2%0AYPtt71Tr3H8kxHPUACC3KNTCxw%3D%3D%0A)
        
          
        ​
        
    8.  Click "Run options" and make sure you get a successful response like the following:
        
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/832731234/26e80339806f48d36f2e51c1/image.png?expires=1784851200&signature=e678fea995c0a8fcb75ac70462e0de67cb3ec327f7c13bd56a49e3bebcd639d4&req=fCMlEcp%2Fn4JbFb4V1XW4gQVHytLkX2lb0pD7fBhOWt92WHnZvHP2UpZjlXIq%0AEhTkkiXuRMFXCt8f5nIGiQxaVw%3D%3D%0A)
        
          
        ​
        
    9.  **Location of token from response:** `SPLIT(cybereason_cookie.headers["set-cookie"], ";") |> FIRST(%)`
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/832729358/b9a0693e9b30fd7d25bfb7a5/image.png?expires=1784851200&signature=e2a4cc47c3a6387c0facceaeb11b3318f8120e82c8884349e69f5ac38ac8da68&req=fCMlEct3noRXFb4V1XW4gSIY0n6UCICRypg9lLQji7LO%2BYRquKpxDyLmW59A%0AmAI1bzhcDSROi9lAE6Tir2onXQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/832729482/676fd102dae93c0127312635/image.png?expires=1784851200&signature=73cb46d241b67daa662bc84350ccd52ec6f471ebee41b5d93fb436b95a97e514&req=fCMlEct3mYldFb4V1XW4gRzNH2kaxcGUm7DxSnF5gZI%2FSwVvHrBfHkAgBNih%0A1Wlx%2FFnSmNgWGAAe5VqVI1RJXQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/832730309/cc7c62cf25275fa3417fadf3/image.png?expires=1784851200&signature=6e62769eedb52e68822c89ffc872124ba57b87a1e9dc174a2bd22ddff063fcc9&req=fCMlEcp%2BnoFWFb4V1XW4gZjcg14d7lMEYycoybsHRzNG7KFtkinGf1PqB7EL%0AtW273GEM3u8NU%2BTJuGwc2MEVzg%3D%3D%0A)

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Cybereason stories in the [story library](https://www.tines.com/library?s=cybereason&view=all).**

### Using the credential in an action

The Header configuration for your Cybereason credential should be:

```
"Cookie": "<<CREDENTIAL.cybereason_cookie>>"
```

Here is an example Cybereason action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Retrieve Cybereason Sensor Information","description":"Retrieve sensor infomration","options":"{\"url\":\"https://<<RESOURCE.cybereason_url>>/rest/sensors/query\",\"method\":\"post\",\"content_type\":\"json\",\"payload\":{\"limit\":1000,\"offset\":0,\"filters\":[]},\"headers\":{\"Cookie\":\"<<CREDENTIAL.cybereason_cookie>>\"}}","position":{"x":375,"y":1095},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"2dca49465829825a3f4f5ddaee6b1321c9667ffa07d713ab8186b60a9be3d607","createdFromTemplateVersion":1,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```

## Cylance Authentication Guide

**Cylance** was a pioneer in cybersecurity AI, replacing legacy antivirus software with preventative solutions and services that protect the endpoints—and businesses. While other security providers claim to use AI in their products, they fall short of the full promise of AI to prevent future cyberattacks.

## First, get your Cylance application details

1.  Log into your Cylance console as an Administrator
    
2.  Select "Settings" and click "Integrations"
    
3.  Click "Add application"
    
4.  Name the application "Tines"
    
5.  Provide the access privileges for the actions you want to be able to take in Tines:
    
    ```
    - device:list 
    - threat:read 
    - threat:devicelist
    ```
    
    ![](https://downloads.intercomcdn.com/i/o/768987856/27a40ffc622794ba026ed8a0/image.png?expires=1784851200&signature=1b27ecf379c5ad1bce40a9e02a13e84eabadc843ac995b5d00a48184a9bd5d95&req=cyYvH8F5lYRZFb4V1XW4gb3J1ijMBMr6t8ZfIgtjSuN5vCR6ShrkiNcFo%2Fho%0Ag2GMH8Bb8ZIUyOeO%2FjEZkw3dvg%3D%3D%0A)
    
6.  Click "Save" and copy the "Application ID" and "Application secret".
    

## Next, create a Cylance credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "JWT"
    
4.  Input the values for the credential
    
    1.  **Name**: Required
        
    2.  **Description**: Optional
        
    3.  **Algorithm**: HS256
        
    4.  **Payload:**
        
        1.  **iss:** Represents the principal issuing the token, which is [http://cylance.com](http://cylance.com)
            
        2.  **jti**: Unique ID for the token, which can be used to prevent reply attacks. This can be a random string
            
        3.  **sub** - Principal subject to the claim, which would hold your Cylance "Application ID".
            
        4.  **exp:** `<<DATE(\\"now\\", \\"%s\\") |> PLUS(%, 1800)>>`
            
        5.  **iat:** `<<DATE(\\"now\\", \\"%s\\")>>`
            
    5.  **HMAC Secret**: "Application Secret" copied earlier
        

**Important Note:** Do not choose the option to automatically generate these values in Tines. Use the [DATE() formula](https://www.tines.com/docs/formulas/functions/date) to generate both `exp` and `iat`.

![](https://downloads.intercomcdn.com/i/o/768992448/641d3f3e50a00cef7c0107ff/image.png?expires=1784851200&signature=e0ae66a543251170eb495d56f7ee7b6cb0c4f141ad4a36ac9951657d73f47b9a&req=cyYvH8B8mYVXFb4V1XW4gUNmua0sJB5TlzBSz3eG4NVfi3e7Jpj%2FByqJTcha%0AzRtRCWFH3%2BEUfah5N7jwyGGagg%3D%3D%0A)

## Using the credential in an action

Exchange the JWT credential for a Cylance access token using the Auth Token action below as the first step in your story:

```
{"standardLibVersion":"28","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Auth Token","description":null,"options":"{\"url\":\"https://protectapi.cylance.com/auth/v2/token\",\"method\":\"post\",\"payload\":{\"auth_token\":\"<<CREDENTIAL.cylance>>\"},\"content_type\":\"json\"}","position":{"x":375,"y":240},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null}],"links":[],"diagramNotes":[]}
```

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Cylance stories in the [story library](https://www.tines.com/story-library?s=cylance).**

  
​

## Darktrace Authentication Guide

Connect Tines to Darktrace to create powerful automations with the below authentication guide.

### First, get the relevant credentials needed in Darktrace

To authenticate Darktrace for use with Tines, you will need the following:

-   Public token
    
-   Private token
    
-   Darktrace domain
    

Public and private tokens can be generated on a per-user basis or via Global tokens. For further details, visit [Darktrace's Authentication Info Page](https://portal.darktrace.com/product-guides/main/api-tokens).

### Then, create a Darktrace credential in Tines

**Darktrace connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Darktrace" and follow the prompts to connect.
    

##### **Manual credential creation**

This method is not recommended as the Darktrace connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Darktrace credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your private token
        
    4.  **Metadata:** Enable metadata and add the below metadata:
        
        ```
        {
          "domain": "your Darktrace domain",
          "public_token": "your Darktrace public token"
        }
        ```
        
        This will allow you to reference the credential's metadata via the `INFO` key. This is also used in our templates.
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains. As Darktrace uses signature based authentication, the secret is used to create a signature and cannot have a restricted domain. Do not enter a value for this field.
        
    2.  **Access:** What other teams can also use the credential.
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/darktrace).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).

## Data transformation example - Automatic mode

## Overview

**Automatic mode** lets you ask Tines to decide when and how to apply transformations to your data. It’s like telling Tines to handle the heavy lifting automatically without you having to manually trigger each transformation. All you need is a little bit of prompt engineering!

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers

-   [Tines - Automatic Mode](https://www.tines.com/docs/actions/types/event-transformation/automatic/)
    

## Make it happen

### Scenario

Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!). **Run** them and see how Automatic Mode outputs results within the **Events** panel:

-   **Fruit Example:** Using Automatic Mode, we loop through an array of fruits to insert each array value into a message.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1451039821/4d1a8968ae66a1067ea22bd65aa2/image.png?expires=1784851200&signature=0d0a235c6f8094afcfba6681d72169bcc4ad478b0b2aadac71c34cb961a13e88&req=dSQiF8l9lIldWPMW3nq%2BgZLZqnA8ooUUExZiEFIlW7YcogqX0w9H3cAH%2BjAw%0ASgYvqM0doZCc9yOIbVnLl07QRSk%3D%0A)
    
-   **Email Example**: Using Automatic Mode, we extract the URLs presented within an email body.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1451042695/a31b8e32f87d9728aac21a2f9849/image.png?expires=1784851200&signature=fce0ca1254ed3fd9bbaf77e5f351d4e2748d96c0c490f9ebbd93bce1d8d52bdf&req=dSQiF8l6n4dWXPMW3nq%2BgUaL8rpESX4aIzh0E0BX9CmuTI0gjR6W%2FW3qKSFl%0AXtX%2BlOs%2B1iBqD58usgUD1hRCRoE%3D%0A)
    

* * *

### Actions

#### Fruit Example

```
{"standardLibVersion":"81","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Automatic","description":null,"options":"{\"mode\":\"automatic\",\"input\":\"=fruit\",\"guidance\":\"Write the following sentence for each element in the array, replacing the fruit and color.\\n\\n\\\"The fruit is color.\\\"\",\"language\":\"python\",\"script\":\"def main(input):\\n sentences = [f\\\"The {fruit.get('name')} is {fruit.get('color')}.\\\" for fruit in input]\\n return sentences\"}","position":{"x":1140,"y":900},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Fruit","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":[{\"name\":\"banana\",\"color\":\"yellow\"},{\"name\":\"strawberry\",\"color\":\"red\"},{\"name\":\"orange\",\"color\":\"orange\"}]}","position":{"x":1140,"y":825},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"1","receiverIdentifier":"0"}],"diagramNotes":[]}
```

#### Email Example

```
{"standardLibVersion":"81","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Email Message","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"body\":\"<!DOCTYPE html>\\n<html>\\n<body>\\n <p>Hey [Recipient's Name],</p>\\n\\n <p>Hope you're doing well! Just wanted to send over a few links that might come in handy.</p>\\n\\n <p>The <strong>project docs</strong> are over here: \\n <a href=\\\"https://www.notion.so/project-docs\\\">Notion Docs</a>.</p>\\n\\n <p>If you need to check on any tasks or update your progress, you can do that on \\n <a href=\\\"https://trello.com/b/taskboard\\\">Trello</a>.</p>\\n\\n <p>Our <strong>meeting schedule</strong> is all on \\n <a href=\\\"https://calendar.google.com/calendar/u/0/r\\\">Google Calendar</a>.</p>\\n\\n <p>And if you run into any issues, the <strong>FAQ page</strong> might have the answers: \\n <a href=\\\"https://support.microsoft.com\\\">Microsoft Support</a>.</p>\\n\\n <p>Let me know if you need anything else!</p>\\n\\n <p>Cheers,<br>[Your Name]</p>\\n</body>\\n</html>\\n\"}}","position":{"x":1335,"y":825},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Automatic","description":null,"options":"{\"mode\":\"automatic\",\"input\":\"=email_message.body\",\"guidance\":\"Extract the URLs from this message.\",\"language\":\"python\",\"script\":\"import re\\n\\ndef main(input):\\n # Use regex to find all URLs in the input HTML\\n urls = re.findall(r'href=\\\"(https?://[^\\\"]+)\\\"', input)\\n return urls\"}","position":{"x":1335,"y":900},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"0","receiverIdentifier":"1"}],"diagramNotes":[]}
```

## Data transformation example - Data mapping

## Overview

**Data mapping** in Tines is about connecting the data you receive to the places where it’s needed in your workflow. This is done using **reference paths**, which allow you to pinpoint exactly where the data you want is located within the incoming event data.

When Tines receives data (often in JSON format) from another system, you need to map the incoming fields to the correct spots in your workflow. This is done using **reference paths**; like giving Tines directions to find the data in the JSON response.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Referencing data](https://www.tines.com/docs/formulas/referencing-data/)
    

## Make it happen

#### Scenario

[Watch video](https://www.youtube.com/embed/kSKS3VgAsxQ?rel=0)

Reference employee data from a previous action.

1.  Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!).
    
2.  Click the “Employee” action tile.
    
3.  Review the JSON structure of this action via the **Payload Builder** in the right config panel to familiarize yourself with the data it will pass.
    
4.  Click the “Referencing Data” action tile.
    
5.  Review the **Payload Builder** of this action as well. Notice how the Value pills are referencing the upstream “Employee” action tile’s JSON paths.
    
6.  Click back to the “Employee” action tile.
    
7.  Click **Run**.
    
8.  Once the flow has finished, compare the **Events** of both actions and see how the upstream data is being referenced in the “Referencing Data” action.
    

* * *

#### Actions

```
{"standardLibVersion":"82","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Employee","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"user\":{\"name\":\"Angie\",\"email\":\"angie@tines.io\",\"profile\":{\"city\":\"Boston\"}}}}","position":{"x":165,"y":285},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Referencing Data","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":\"The user's name is <<employee.user.name>>. Their email address is <<employee.user.email>>. They work at the <<employee.user.profile.city>> office.\"}","position":{"x":165,"y":375},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"0","receiverIdentifier":"1"}],"diagramNotes":[]} 
```

## Data transformation example - Data structures

## Overview

Creating a **data structure** means organizing your data into a format that is easy to use in your workflow. In Tines, this involves defining a **data type**, which describes the structure of the data you’re working with.

Data types in Tines are **schema-less**, meaning that the data doesn't have to follow a rigid structure. However, defining a clear structure helps to make the data easier to understand and work within your automation.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Language description](https://www.tines.com/docs/formulas/language/)
    

## Make it happen

#### Core Data Types in Tines

-   **Text**: A string of characters (e.g., a name, description, etc.). This data structure is colored purple within Tines.
    
-   **Integer**: A whole number (e.g., 123). This data structure is colored green within Tines.
    
-   **Float**: A number with decimals (e.g., 12.34). This data structure is colored green within Tines.
    
-   **Boolean**: A true/false value. This data structure is colored blue within Tines.
    
-   **Array**: A list of values, which can be of any type (e.g., an array of users or event data). This data structure is identified by square brackets (“\[“, “\]”)
    
-   **Object**: A collection of key-value pairs (e.g., an object containing user information like name, email, etc.). This data structure is identified by curly brackets (“{“, “}”).
    

* * *

#### Extra Information

You might be asking, why is it important to identify these different data structure types in Tines? Here are a few reasons!:

-   **Troubleshooting:** If you misinterpret the structure, you may run into errors like trying to access an array as an object. Recognizing data structures helps in troubleshooting Tines functions and API responses effectively.
    
-   **Efficient data parsing:** Understanding JSON structures helps you extract and manipulate data correctly, whether it's a simple key-value pair, an array, or a deeply nested object.
    
-   **Interoperability:** As you’ll be working with different platform APIs in Tines, endpoint parameters may require that their value is set to a specific data structure. Familiarizing yourself with these will make it easier to work with any endpoint.
    

* * *

#### Scenario

![](https://downloads.intercomcdn.com/i/o/am0g137l/1450995457/028cf03ba4a9c379c6851a21a205/image.png?expires=1784851200&signature=ed73eecb7e422dff1305d4fae9765f87c15be2353da0ef90fa87d90ed41ff306&req=dSQiFsB3mIVaXvMW3nq%2BgSizdtBe9ZUa7Og89DeAj1VX5nU%2FwsU6A0B9xN5N%0ABOY71gLm2Rg775T29sAaapemBM0%3D%0A)

Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!). **Run** them and see how the different data structures look within the **Events** panel.

* * *

#### Actions

```
{"standardLibVersion":"82","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Data Types","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"array\":[\"This is an array structure!\"],\"boolean\":true,\"float\":17.38,\"integer\":21,\"null\":null,\"object\":{\"value\":\"This is an object structure!\"},\"string\":\"This is text!\"}}","position":{"x":750,"y":1080},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Real Example","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"user\":{\"id\":42,\"email\":\"yanni@tines.io\",\"is_active\":true,\"survey_score\":96.5,\"favorite_foods\":[\"ramen\",\"burritos\"]}}}","position":{"x":915,"y":1080},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[],"diagramNotes":[]}
```

## Data transformation example - Functions

## Overview

**Functions** in Tines allow you to perform calculations, manipulate strings, and work with data in creative ways. They are essential for transforming data in your workflows and can be applied to data at any point in the automation process.

Tines has a set of **built-in functions** that you can use to manipulate data, here are some examples:

-   **Mathematical Functions**: Functions like `SUM`, `SUBTRACT`, `TIMES`, and `DIVIDED_BY` allow you to perform operations on numbers.
    
-   **Text Functions**: Use functions like `CONCAT`, `TITELIZE`, and `DOWNCASE` to manipulate text. These functions are useful for formatting data or combining text values.
    
-   **Logical Functions**: Functions like `IF`, `IS_PRESENT`, and `OR`, and `SWITCH` allow you to create conditional logic in your workflows.
    
-   **Time Functions**: Functions like `NOW`, `DATE`, and `DATE_DIFF` allow you to manipulate date and time data.
    

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Functions](https://www.tines.com/docs/formulas/functions/)
    

## Make it happen

### Scenario

[Watch video](https://www.youtube.com/embed/v7_0sEDiGvA?rel=0)

Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!). **Run** them and see how the different Tines functions executed within the **Events** panel:

-   Utilize the `IF` function to return a message based on a passed value.
    
-   Utilize the `DIVIDED_BY` function to divide two numbers.
    
-   Utilize the `TITLEIZE` function to capitalize the first letter of each word in a string of text.
    
-   Utilize the `NOW` function to return the current date and timestamp in UTC.
    

* * *

### Actions

```
{"standardLibVersion":"81","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Functions","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"logical\":\"=IF(data.color = \\\"Blue\\\", \\\"Blue is my favorite color!\\\", \\\"This isn't my favorite color.\\\")\",\"mathematical\":\"=DIVIDED_BY(data.number, 2)\",\"text\":\"=TITLEIZE(data.message)\",\"time\":\"=NOW()\"}}","position":{"x":165,"y":1095},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Data","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":{\"color\":\"Blue\",\"number\":8,\"message\":\"welcome to tines!\"}}","position":{"x":165,"y":1005},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"1","receiverIdentifier":"0"}],"diagramNotes":[]}
```

## Data transformation example - Function stacks using |> %

## Overview

A **function stack** is a sequence of multiple functions applied one after another. The `|>(%)` pipeline operator applies multiple transformations to the data in a chain, making it cleaner and more readable.

Functions stacks are used as a sequencer to apply multiple functions one after another (`|>` being the separator) on the output value (`%`) of the previous function. Think of it as numbered instructions for a cooking recipe, where you must do step one before moving to step two.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Operators](https://www.tines.com/docs/formulas/operators/)
    

## Make it happen

### Scenario

![](https://downloads.intercomcdn.com/i/o/am0g137l/1451016013/8ff4bde9cba4ae40762f1f020cdb/image.png?expires=1784851200&signature=f079c7cc437fede89653908185a88a205b9bc1bca1ca942b53828c5ea060991a&req=dSQiF8l%2Fm4FeWvMW3nq%2BgScGWKfdRxQ%2FH1bwksc2Wof21Mw4KhKQW1g38eAZ%0Adq%2FYL811qMwXto2e0nYx2SZmVME%3D%0A)

Map user names in an array, then sort them alphabetically.

1.  Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!).
    
2.  Click the “Users” action tile.
    
3.  Review the JSON structure of this action via the **Payload Builder** in the right config panel to familiarize yourself with the data it will pass.
    
4.  Click the “Pull names and sort alphabetically” action tile.
    
5.  Review the **Payload Builder** of this action as well. Notice how the formula stack utilizes the `MAP` function, which pulls the list of “name” values from the array, then the `SORT` function to sort the names alphabetically. The logic of these functions is separated by the `|>` and the `%` is used to reference the output of the first function (`MAP`) in the stack to be used in the `SORT` function.
    
6.  Click back to the “Users” action tile.
    
7.  Click **Run**.
    
8.  Once the flow has finished, compare the **Events** of both actions and see how the function stack worked to transform the data.
    

* * *

### Actions

```
{"standardLibVersion":"81","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Users","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":[{\"id\":\"01\",\"name\":\"Hanna\",\"team\":\"CSM\"},{\"id\":\"02\",\"name\":\"Shelby\",\"team\":\"CSE\"},{\"id\":\"03\",\"name\":\"Ray\",\"team\":\"Training\"},{\"id\":\"04\",\"name\":\"Daniel\",\"team\":\"Support\"}]}","position":{"x":690,"y":750},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Pull names and sort alphabetically","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":\"=MAP(users, \\\"name\\\") |> SORT(%)\"}","position":{"x":690,"y":825},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"0","receiverIdentifier":"1"}],"diagramNotes":[]}
```

## Data transformation example - Loop to restructure array

## Overview

A **loop** is a powerful tool in Tines that allows you to go through each item in an **array** and apply changes or actions to each one. This is useful when you have a collection of items (like a list of users or events) and you need to process each item in a specific way. The loop helps you to:

-   Iterate over every item in an array
    
-   Restructure the data
    
-   Perform a specific task on each item
    

In Tines, a loop action allows you to run through an array of data step by step and apply a transformation to each item. Each iteration of the loop processes a single element of the array at a time. Think of it as a way to break down larger chunks of data into smaller, manageable tasks that Tines processes sequentially. The loop action is particularly useful when the array has multiple objects or entries and you need to apply changes to each one.

You can enable the LOOP within an action by going to the action's options at the bottom of the properties panel. From there, you'll need to assign an array to the value pill of the LOOP. Your array will now be available within your action by referencing LOOP.value.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Event Transform Action - Looping](https://www.tines.com/docs/actions/types/event-transformation/message-only/#looping)
    

## Make it happen

### Scenario

[Watch video](https://www.youtube.com/embed/exbHQEnMoJs?rel=0)

Loop through an array of fruits to insert each array value into a message.

1.  Copy (`CMD + c`) the JSON block provided below and paste (`CMD + v`) it onto the storyboard (these are action tiles and will auto-populate!).
    
2.  Click the “Fruits” action tile.
    
3.  Review the JSON structure of this action via the **Payload Builder** in the right config panel to familiarize yourself with the data it will pass.
    
4.  Click the “Loop” action tile. Notice how the **Loop** option is enabled and is pointing to the “Fruit” action’s array. `LOOP.value` is the current iteration of the “Fruit” array (iteration one would be “`{“name”: “banana”, “color”: “yellow”}`” and so on). We are asking it to print the “name” and the “color” of each fruit.
    
5.  Click back to the “Fruit” action tile.
    
6.  Click **Run**.
    
7.  Once the flow has finished, compare the **Events** of both actions and see how the upstream data in “Fruit” is being referenced in the “Loop” action.
    

* * *

### Actions

```
{"standardLibVersion":"81","actionRuntimeVersion":"32","agents":[{"disabled":false,"name":"Fruit","description":null,"options":"{\"mode\":\"message_only\",\"loop\":false,\"payload\":[{\"name\":\"banana\",\"color\":\"yellow\"},{\"name\":\"strawberry\",\"color\":\"red\"},{\"name\":\"orange\",\"color\":\"orange\"}]}","position":{"x":1230,"y":420},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"},{"disabled":false,"name":"Loop","description":null,"options":"{\"mode\":\"message_only\",\"loop\":\"=fruit\",\"payload\":\"The <<LOOP.value.name>> is <<LOOP.value.color>>.\"}","position":{"x":1230,"y":495},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null,"tags":[]},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"templateTags":[],"originStoryIdentifier":"cloud:0a13dfc5dbb71212cab0ad314b7fe531:f0064f807ac02bf877a6ff31d9b76916"}],"links":[{"sourceIdentifier":"0","receiverIdentifier":"1"}],"diagramNotes":[]}
```

## Data type conversion

## Converting a string into an integer in Tines?

To convert a string into an integer in Tines, you can use the `NUMBER` function.

> To learn more about the `NUMBER` function with examples, see [here](https://www.tines.com/docs/formulas/functions/number).

## Ensuring your data maintains its original format

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, inside the builder window. 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.

> Read more about using pills and formulas [here](https://explained.tines.com/en/articles/8944051-formulas-and-pills-explained).

## Text mode v Formula mode

You can choose the language type in the builder to control the data type of the output:

-   `text_mode`: If you have a value in text mode, it will treat the output as a text string.
    
-   `formula`: If you set the value to formula mode, it will treat the output as an object (array or JSON object, depending on the output of the functions used)
    

You can change the language type in the builder as shown below:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2234031862/36725f5211b74bc45e46d811a1b9/41fca7a5-5e06-45be-a1c0-a1c931c9281f?expires=1784851200&signature=7801b0550b09028531a386e627c94377c589c26154d3f6686710b6c697a8d872&req=diIkEsl9nIlZW%2FMW3nq%2BgQfpHZWqcv4Y0mgoWxFnUaNJeedX%2B7QHMP%2FpBYdM%0Ap0TUUMfMWzRSFazpxItsNtUX1QY%3D%0A)

Sample event output:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2234031863/270f9f6f3c76a4e900ed1297e730/f790502a-a65e-4004-8d33-5284eeec9f28?expires=1784851200&signature=bbbaf90c05de26e49e493f1d27fafb5455af2a3fc0b9113e6f2c23279c1d41f8&req=diIkEsl9nIlZWvMW3nq%2BgegMSEafJoQFx0boYHiQs8WEv7Fwcveo1%2BBOMtp5%0ATCQ5ZSHQH9z6L4Eecm1VJykbDkM%3D%0A)

In this example:

-   `text_mode` treats the output as a text string,
    
-   `formula` outputs an array object

## Date and time operations

## What timezone does DATE("now") function return?

`DATE("now")` will return a UTC time.

> Read about the `DATE` function in our [docs](https://www.tines.com/docs/formulas/functions/date/).
>
> You can find an example of `DATE` function uses in the [story library](https://www.tines.com/story-library/87675/manage-time-formats-in-tines)

## How can I convert timezones in Tines?

There are two examples in the action below,

-   UTC to PST (28800 seconds difference)
    
-   UTC to PDT (25200 seconds difference)
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2234212009/09699817c703df70a698d5837da5/85fc127a-8829-4ea1-a574-d6154375c1ec?expires=1784786400&signature=4ea6f7927c738bba80ad86a4e9099d67a9513dd9604c05540d7d79da6524a0c5&req=diIkEst%2Fn4FfUPMW3nq%2BgYXH%2FBmtFtkhYHhTjYl4dr3GEGey46vWt%2FhEoWeL%0ApVdLgAxFhUJmNpJV%2BRtNz%2BhqIGU%3D%0A)

You can copy/paste the below code into your story board:

```
{"standardLibVersion":"20","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Timezone","description":null,"options":"{\"mode\":\"message_only\",\"PST\":\"<<DATE(\\\"now\\\", \\\"%s\\\") |> MINUS(%, 28800) |> DATE(%, \\\"%Y-%m-%dT%H:%M:%S\\\")>>\",\"PDT\":\"<<DATE(\\\"now\\\", \\\"%s\\\") |> MINUS(%, 25200) |> DATE(%, \\\"%Y-%m-%dT%H:%M:%S\\\")>>\"}","position":{"x":705,"y":780},"type":"eventTransformation","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"spotlightTemplate":null,"spotlightWriters":[],"form":null,"cardIconName":null,"cardIconColor":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null}],"links":[],"diagramNotes":[]}
```

## Converting time into UNIX or epoch time

To convert time into UNIX time, use the function `DATE("now", "%s")`

## Calculate the difference between two dates

### Option 1: Using formulas

You can calculate time differences using the `DATE_TO_UNIX` formula:

`=DATE_TO_UNIX("2026-04-15") - DATE_TO_UNIX("2026-01-01")`

  
This gives you the difference in **seconds**. Then divide:  
÷ `60` → minutes  
÷ `3600` → hours

÷ `86400` → days

Example in an event transform (automatic mode):

```
{ 
"diff_in_days": "=DIVIDE(MINUS(DATE_TO_UNIX(date_two), 
DATE_TO_UNIX(date_one)), 86400)"
}
```

### Option 2: Using a run script action (python)

If you need more precision or complex date math, a **python script** is very flexible:

```
from datetime import datetime
date1 = datetime.strptime("#{{ date_one }}", "%Y-%m-%d")
date2 = datetime.strptime("#{{ date_two }}", "%Y-%m-%d")
diff = (date2 - date1).daysprint(diff)
```

> To learn more about the date function, [see here](https://www.tines.com/docs/formulas/functions/date)
>
> You can find an example of DATE function in the [story library](https://www.tines.com/story-library/87675/manage-time-formats-in-tines)

## Digital Guardian Authentication Guide

Digital Guardian is an American data loss prevention software company that offers products to detect and prevent malicious actions by users and malware on endpoints. Its software is designed for both individual users and corporate networks, servers, databases, and the cloud.

## First, get Digital Guardian credentials

You will need to have the following (found in your DGMC environment by navigating "System" -> "Configuration" -> "Cloud Services" from the DGMC menu.

-   Access Gateway Base URL
    
-   Authorization Server URL
    
-   API Access ID
    
-   API Access Secret
    

## Lastly, create a Digital Guardian resource and credential in Tines

You will need to create:

-   One resource: "Digital Guardian URL"
    
-   One HTTP request type credential: "Digital Guardian"
    

##### Resource

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Digital Guardian resource
    
    1.  **Name:** Digital Guardian URL
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your "Access Gateway Base URL"
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

##### HTTP request type

1.  Navigate to the team that will be using the API and click "Credential"
    
2.  Click "+ New Credential" and select "HTTP Request"
    
3.  Input the values for the Digital Guardian credential
    
    1.  **Name:** Digital Guardian
        
    2.  **Description:** Optional
        
    3.  **URL:** Your "Authorization Server URL, i.e. `https://authsrv.msp.digitalguardian.com/as/token.oauth2`.
        
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** Replace `<<api_access_id>>` with your "API Access ID"  
        ​  
        ​
        
        ```
        {
          "client_id": "<<api_access_id>>,"
          "client_secret": "<<secret>>",
          "grant_type": "client_credentials",
          "scope": "client"
        }
        ```
        
    7.  **Secret:** Your "API Access Secret"
        
    8.  Click "Save"
        
4.  Click "Run request" and double-click on the `access_token` key to copy the path.
    
5.  **Location of token from response:** Paste the path to the token in the value pill, `digital_guardian.body.access_token`
    
6.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
7.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/947482768/a28299cbfb1f69336d0828c7/image.png?expires=1784851200&signature=ef5db26cc4dd3abb00dbde332f10265644adea2a6664a28475b6db9875ec1051&req=fSQgEsF8modXFb4V1XW4gYX4fmOaUfSXMQSzC1QQzHJ3iejLncJ4oxP1rS4f%0Ap8yzIYxdWnxVKEROWkSxnK0zhA%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/947482901/8def48f0ab65ceb3e0d0de7f/image.png?expires=1784851200&signature=9563c109f0fe62b01ef57a50de6c640ba814cc285231d004c4f051e34de80976&req=fSQgEsF8lIFeFb4V1XW4gUiDxnvAAsJ0FN4emO%2FBEPsFbJhrP9HzuSFn%2BmVh%0AQTCcGpw%2Fa1NUeaglAuUQUEVjiQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/947483015/5aaccb2f460d3dd6707f3a5b/image.png?expires=1784851200&signature=18be1da9003879ee4938ddc6e2eae67468e5798e247e9d5c51873f530b400c42&req=fSQgEsF9nYBaFb4V1XW4gYlBg5kSEfy5FvW0y7QK6bzNZNe4zdj02M7DqFjb%0Ani%2FcxSM%2Bx67dEs4vWTjYYTs8YA%3D%3D%0A)

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Digital Guardian stories in the [story library](https://www.tines.com/story-library?s=digital+guardian).**

## Does Tines have a limitation on how often a scheduled action can run?

The shortest frequency for [scheduling an action](https://www.tines.com/docs/actions/configuration#common-config) is one minute. The longest frequency (besides setting up a cron schedule) is once a month:

![](https://downloads.intercomcdn.com/i/o/1003457000/e6d36c293d50fb58432eb4fb/image.png?expires=1784851200&signature=3e7989d08d14375defbfcbaa01741fe53a2075059c5aa1ea5b00bf5d55d339de&req=dSAnFc17moFfWfMW3nq%2BgQOx%2Fi11VYnoI8qmty6JQy5mptiC6hhmLYzBClIx%0AmVmE4m55MgnZV1uQaIF043H71sE%3D%0A)

> **Note:** Each story run is independent and should not impact other story runs. If you are scheduling your actions on a short frequency, we recommend reducing your story event retention (via **Keep events and logs**) to avoid hitting any potential tenant event limits.

## Drata Authentication Guide

**Drata** automates your compliance journey from start to audit-ready and beyond and provides support from the security and compliance experts who built it.

## First, get your Drata API Key

1.  Log into the Drata dashboard [https://app.drata.com/dashboard](https://app.drata.com/dashboard)
    
2.  Click on your username on the bottom left, then "Settings"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769833609/bee93a70ef1f8fb41d65da68/image.png?expires=1784851200&signature=8f9f776a1284c2e6f28a8c64d8c0fbae4ec708f3241800dc057dd6706c49c59d&req=cyYuHsp9m4FWFb4V1XW4gb%2BFGO1bxVrslMMxvonDw3DfR6%2FHJiUa4kGkuFIs%0AHN%2F4icX9UXhSFnvSJlv%2BNnQsOA%3D%3D%0A)
    
3.  Click "API Keys"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769833831/991f78f6b32fdab0dbb53d7c/image.png?expires=1784851200&signature=06a22c069b2eb97c829994b236da18edc76dde62001a77ef58ef044412f692d2&req=cyYuHsp9lYJeFb4V1XW4ga45RR9LJ72uejzVo9YPZk7Zl8EGnLnAplLbJdw7%0Aizg4lvEEoHPu2SdFSrKeYoUyKw%3D%3D%0A)
    
4.  In the top right, click on "Create API Key"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769834121/c128ce3f794ed49ee8734d75/image.png?expires=1784851200&signature=449dbf103ef0d7a96805d5f96fc4193b70d0be1bf913da52e02c94de5dc768fb&req=cyYuHsp6nINeFb4V1XW4gR2e6GkuUziLDzZSIy5g3%2BgQNzzm5Scyik1glgyc%0AQmzZlNx2mACk9Pdw%2Bp6ULfGSvg%3D%3D%0A)
    
5.  Name the key and choose the appropriate scopes for the key, and then click "Save"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769834658/30690aa38aa9667ea37ea89a/image.png?expires=1784851200&signature=4da799a6b8fa70f5c0ef15ee37a8baae219fbecd25833cf27f37e6f200af0503&req=cyYuHsp6m4RXFb4V1XW4gU0SmYRSwaTCi7jP4Ie5wVpAzLL1kAuY813BOHOQ%0APiHGDsO%2FMRRrM44AAiy9iGWphA%3D%3D%0A)
    
6.  Copy the API Key, check the box and click "Done"
    
    ![](https://downloads.intercomcdn.com/i/o/769835030/46e93ca26e0bdedb78dfa956/image.png?expires=1784851200&signature=8128c0ac1447c07e812ea4dde13c8a2efd874bf911e0417f239f3c9c35020187&req=cyYuHsp7nYJfFb4V1XW4ge%2FrFh9XouxBcqppO%2F4n1%2BlkL87glJUE%2FNfWHsvJ%0AH1QGvAWfOj0zshQJ0CwM0abf6g%3D%3D%0A)
    

## Then, Create a Drata credential in Tines

**Drata connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Drata" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Drata connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Drata credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/drata).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Drata stories in the [story library](https://www.tines.com/story-library?s=drata)**

## Elastic Security Authentication Guide

Elastic Security **equips analysts to prevent, detect, and respond to threats**. The free and open solution delivers SIEM, endpoint security, threat hunting, cloud monitoring, and more.

## Create an Elastic Security resource and credential in Tines

**Elastic connect flow:**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Elastic" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/elastic).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Elastic Security** **stories in the [story library](https://www.tines.com/library?s=elastic%20security&view=all)**

## EmailRep Authentication Guide

**EmailRep** uses hundreds of factors like domain age, traffic rankings, presence on social media sites, professional networking sites, personal connections, public records, deliverability, data breaches, dark web credential leaks, phishing emails, threat actor emails, and more to answer these types of questions.

## First, get your EmailRep API Key

1.  Navigate to [https://emailrep.io/](https://emailrep.io/) and click "API Key"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769864132/f9d5ec69eb1081d670661574/image.png?expires=1784851200&signature=30781e79ccd1533e576fc53dc3c6c42d607a199dfd9184ecc8748bbe21eb5455&req=cyYuHs96nIJdFb4V1XW4gXAy4Ly%2BZ3%2Bu31OQeQWvhroOtW6XRYpLarZVfmdT%0A70X%2B5ayLl4aRMAH5akob%2B3CKAw%3D%3D%0A)
    
2.  Select the plan that fits your needs.
    
3.  Enter the required details to receive your API key  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/769864507/251aadf495d772674acf5953/image.png?expires=1784851200&signature=9fd13584e6399d132665015a5a45aec22bedda4d3ce90adf0a098fb4509780c4&req=cyYuHs96mIFYFb4V1XW4gdTd%2F4mNKjLUuBvVIpsp6bM14mIL7BjmgWT3XLXg%0AjCRnSHht0oBtPvT%2FsFylTme76Q%3D%3D%0A)
    

## Then, create an EmailRep credential in Tines

**EmailRep connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "EmailRep" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the EmailRep connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the EmailRep credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/emailrep).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of EmailRep stories in the [story library](https://www.tines.com/story-library?s=emailrep)**

## Encoding UTF8 on action run

Some users get this error message:

> Could not create event – PG::CharacterNotInRepertoire: ERROR: invalid byte sequence for encoding "UTF8" error

This can happen if we use non-UTF8 characters in an event. All events emitted are UTF8 JSON.

When you BASE64 Decode the string it generates data that cannot be placed into the event due to it not being UTF8 friendly resulting in the error.

  
The reason you can see an output on the test and pill builder preview is because these does not attempt to write the data to an event yet. The error only occurs when trying to place the data into an event to be emitted.

## Ermetic Authentication Guide

Ermetic is an identity-first cloud-native application protection (CNAPP) and infrastructure security platform that automates asset discovery, risk analysis, runtime threat detection, compliance and least privilege remediation.

## First, get an Ermetic API token

1.  Login to your Ermetic account
    
2.  Click on the "Settings" cog and then click the "API" tab  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889080231/514efa64cc625916074ba600/image.png?expires=1784851200&signature=406a00e789c231f053cca1bb1ba39e6ff2c2c89a71ed606623ba43ae14efc7f7&req=fCguFsF%2Bn4JeFb4V1XW4gWPbppb6SbVtrD%2FWZNZsU7OPNuqsLo05rEfaH2fL%0Auy9DiggMIpz4Yrp7A2920t3d1Q%3D%3D%0A)
    
      
    ​
    
3.  Click "+ Create token"
    
4.  Name the token. choose the appropriate role, and click "Create Token"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889080834/2ed16284c34fda9c2392515e/image.png?expires=1784851200&signature=427708e53bdd2096707de888836054b91d7f07ac290f3a130a076ac77c6c4105&req=fCguFsF%2BlYJbFb4V1XW4gaJDefqQhGWqoZjQNynBg8VwZHNQ2tRze3pbu4qD%0An4%2Bd%2B9Y%2FtQDT95iBeBQDY%2B67XQ%3D%3D%0A)
    
      
    ​
    
5.  Copy your token and click "Close"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889081593/14d2f8b5924135b239af1f62/image.png?expires=1784851200&signature=0c822ad7c35c37d54cfbd94951a92b408796fea577703d423ae368f4724f0be9&req=fCguFsF%2FmIhcFb4V1XW4gQffbJn%2F%2BDto9bGTEqPbM8BatAV1Y5SnkmgRPfkY%0A7dpw3znMOv%2FpgblFB7XASWjCEQ%3D%3D%0A)
    

## Lastly, create an Ermetic credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Ermetic credential
    
    1.  **Name:** Ermetic
        
    2.  **Description:** Optional
        
    3.  **Value:** Your token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Ermetic** **stories in the [story library](https://www.tines.com/library?s=ermetic&view=all)**

## "Error evaluating formula: To use this credential with Run Script Actions, add the special "run-script" keyword..." Error

## Overview

While working with credentials in Tines, you may run into the following error:

```
Error evaluating formula: To use this credential with Run Script Actions, add the special "run-script" keyword to the credential's list of supported domains and URLs.
```

This article covers the cause and solution to this behavior.

## Cause and solution

This error occurs when a user attempts to use a credential in a Run Python Script action that doesn't have the `run-script` keyword added in its configuration and domain restriction is enabled.

> 🟢 **Solution:** Add the `run-script` keyword into the credential's **URLs and Domains** setting. Make sure it is space limited. For example:  
> ​
>
> ![](https://downloads.intercomcdn.com/i/o/1171543760/2991c011cd1e83d04bde56e4/image.png?expires=1784851200&signature=5b24879aaedd13db7c9c646aa2bd04ecbd2396aeef9afd2d1d7f35ebf7474ac2&req=dSEgF8x6noZZWfMW3nq%2BgY48LsmYPUFoM4wdcFMqEWBJcosdgVx2RzNh4mCX%0Aot14ekkscI3JVi0PFyihXRdHWng%3D%0A)

## Need further assistance?

Here are resources to help better understand the infrastructure surrounding the behavior:

-   Tines: [Credentials](https://www.tines.com/docs/credentials/)
    
-   Tines: [Domain restriction](https://www.tines.com/docs/credentials/credential-configuration/domain-restriction/)
    
-   Tines: [Run Python script](https://www.tines.com/docs/actions/templates/templates/frequently-used-templates/run-python-script/)

## Error handling: Best practice guide

Effective error handling is crucial for building reliable and resilient stories. This guide outlines the key methods for configuring retries, advanced error logging, and comprehensive monitoring to ensure you are immediately aware of, and can recover from, potential issues in your story.

## Useful resources

-   [This story](https://www.tines.com/library/stories/1244430/?name=using-records-to-improve-story-performance-in-tines) from our Library that utilizes Tines Records to improve story performance.
    
-   [This story](https://www.tines.com/library/stories/1257767/?name=how-to-optimize-tines-stories) in our Library has general best practices around story building (it goes hand-in-hand with the blog below).
    
-   [This blog](https://www.tines.com/blog/optimizing-tines-building-efficiently/) on Optimizing Tines Stories - Tips and Tricks for Building Efficiently
    
-   Tines [University](https://www.tines.com/university/).
    

## Configuring retries and error statuses for HTTP requests

To manage temporary service interruptions, you can configure your HTTP request actions to automatically retry upon failure.

### Set retry on status

> Note: This setting must be enabled for the "Retries" setting (section below) to work.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218433004/7caa67cc4a73ab3e237ae2ebeb23/retry+on+status.gif?expires=1784851200&signature=6f4d67d7b96f8190f91b94e0aefbfa8c1b6c0802a71d204f91de5772da15f35f&req=diImHs19noFfXfMW3nq%2BgdTjCkhXqJAc7c74HdiNZK1DLW8nRbS1hByJJs8A%0AfrkxusjbS4nVQKmoglA6BpNyqfA%3D%0A)

Useful examples include:

-   **Status code 0 (Timeout):** This is useful when a service is completely unreachable and does not send a response.
    
-   **400–499 status codes (Client errors):** These typically indicate user-side issues, such as a 401/403 credential problem or a 404 not-found error.
    
-   **500-599 status codes (Server errors):** These error codes indicate that something is wrong on the server side, such as the service being unavailable.
    

### Set retries

You can set the number of times that Tines will retry your action, up to a maximum of 25.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218433941/2e210959d34f404196472e2cd9f0/retries.gif?expires=1784851200&signature=ea193ad13b746e3e723075ea82e65152f15112decdc346d9ff4269387b0d8199&req=diImHs19nohbWPMW3nq%2BgfZ4ec6ZURXnOk3Gp9GwK3qyL1YmyVLJtjkY2rSa%0AgEsIVDOI7zXS7%2Bm1g0NrNtRwpcM%3D%0A)

These retries can be configured to execute on specific status codes with retry on status, such as 500 errors, which often indicate temporary service unavailability.

Note: The precise calculation for the time between retries is available in the [product documentation](https://www.tines.com/docs/actions/types/http-request/).

### Log error on status

Use the "log error on status" feature to handle specific error types. Similar to retry on status, examples include:

-   Status code 0 (Timeout).
    
-   400–499 status codes (Client errors).
    
-   500-599 status codes (Server errors).
    

### Retries in templates

Even when using templates with a friendly graphical user interface, you can still configure retries by selecting the action, choosing the "look inside" option, and configuring the HTTP parameters to set "retry on status".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218438273/bf476a280dbaddfdd212d40f0a0e/retry+on+template.gif?expires=1784851200&signature=a150cf0897e935dd30ba97aa5d06e4f2a45734345842b367f378362c26413efa&req=diImHs19lYNYWvMW3nq%2BgcDdp5wK1Fca4q1FQ2n3myHZIutzdT4PPOKY1vQ9%0Al%2Fmc5oOUf83bBWzBBLj56hX%2FQ9Q%3D%0A)

## Advanced error logging techniques

Beyond standard status codes, you can implement methods to detect errors that are hidden within a successful response.

### Handling errors in a 200 success body

Some APIs, like Slack, may return a 200 status code even when the request wasn't actually successful. In the example below, omitting the channel ID for a Slack message still results in a 200 status code, but the response body contains an error.

To catch these, use the **log error if** feature. First, you must inspect the event data's body structure to locate the error object.

> Note: The `OUTPUT` variable: The `log error if` function examines the action's `output` before the event is sent. This variable is distinct and cannot look at upstream event actions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218441246/c82e2c9b805a25e88cd177d337fc/slack+error+with+200+status.gif?expires=1784851200&signature=ff3dc1355ca3e7226a73981cb324d15ffc701b3922f4d2fb91996ac14e4b214b&req=diImHs16nINbX%2FMW3nq%2BgVoMir4r69Gw4gSiurtsIeV4aMMWabAfrjrUqpUB%0AOzarTjnTzHj0ZEAueznnFPjs834%3D%0A)

Configure `log error if` to check the `OUTPUT` variable for the presence of this error object path: `IS_PRESENT(OUTPUT.body.error)`. This is the system default when you add this option.

## Monitoring and notification best practices

There are two primary ways to receive alerts about action failures:

-   By setting a failure path on a specific action.
    
-   By setting up story-level monitoring.
    
-   By setting up action-level monitoring.
    

### Action-specific failure path

Configure a separate flow to run after an HTTP request action fails using the "emit failure path" feature.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218447094/eeeca0ac947f2b7f293588db9447/emit+failure+path.gif?expires=1784851200&signature=222cb892a40d0e2e79a7324f0a019a18a0941f745dc5c01fc0fd088f7197b1a7&req=diImHs16moFWXfMW3nq%2BgSRDp1ILzbYnQTG53s3u5YgRCe3YVTIgJX0ivdMF%0AGUgZcJSiwLnEOGikD7oUduA7HZ8%3D%0A)

This creates a dedicated path for sending failure notifications, whether by email, Slack message, or another channel. You can also configure it to an HTTP request action that automatically opens a ticket in an external system like Jira.

### Story-level monitoring

Monitoring can be set up in the story settings to send notifications when an action fails.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218444534/9a7860e3b6b5e089d9dbc57e8481/story+level+monitoring.gif?expires=1784851200&signature=10bfa82ce7c9933f887df7a0e209e6a617ab6ca6f1f5a1cf0b09c4b31850596f&req=diImHs16mYRcXfMW3nq%2Bge16YdGJtFbTrCS7WxqMPRWe9SvOA4q7YmWxfxTN%0AvNzucwnSp3Fi8o0CnStC%2BlIhnpA%3D%0A)

### Entire story failure

Configure the story settings to notify you when any action fails within the story.

### Action-specific monitoring

For finer control, you can set up monitoring for a specific action by going to the action's Status tab and selecting monitoring.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218445933/832fb9ef060b00434415c37d5f23/action+level+monitoring.gif?expires=1784851200&signature=0c4cd904dfb7b2e3cb260c3cb63f943bcfe9d6151efd0330ad90e706630108b6&req=diImHs16mIhcWvMW3nq%2BgTliO64jnbulRuQeroarzrMgt2ndvT4gTw5c7ihA%0AN2KoMVmIIskoIDy5YgDWNni0Pjw%3D%0A)

### Notification methods

Notifications can be sent to:

-   An email address.
    
-   A webhook URL: This allows you to trigger a separate story for customized error handling.
    

> Note: These must be set at the story-level, including for action specific monitoring.

### Tenant health page

You can get a high-level view of story activity in the Tenant health page.

The "Live activity" section there provides a count of errors for each story listed.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218449963/1c6ea53de367fbc1c60c30b761c1/live+activity+erros.gif?expires=1784851200&signature=a15eb65b6ae9590001edc531b832ef5584df443434b589dd231470d78db0d91f&req=diImHs16lIhZWvMW3nq%2BgUfy7ykXInbWXK%2BXQd4taH9RZJyP5D5m5HcPMf8X%0A9HYy5gtoY%2FJIBm9R%2BFOWUWby6e4%3D%0A)

### Best practices for failure notification content

When configuring failure notifications, include crucial troubleshooting data to quickly diagnose the issue. Essential data can include:

-   The date of the failure.
    

> Tip: Utilize the [`DATE`](https://www.tines.com/docs/formulas/functions/date/) + [`NOW`](https://www.tines.com/docs/formulas/functions/now/) functions to dynamically set the date value.

-   The story ID and story name.
    
-   The story URL (to redirect you to the exact story).
    
-   The ID of the story run.
    

> Tip: These 3 can be pulled using the [META variable](https://www.tines.com/docs/formulas/referencing-data/#metadata).

-   The error message.
    
-   The status code.
    

> Tip: These can be pulled via upstream reference paths to the action that returned the error.

> Note on built-in vs. custom notifications. Using the built-in story monitoring for email results in a pre-configured, linked email. Using a webhook, however, allows for greater customization and integration with platforms like Slack.

## Event FAQs

## **What access does a user with a view role have to events?**

A user with a viewer role cannot run or re-emit events. You can look at an event, but hovering over re-emit gives the 🚫 icon.

> **👉 For more details on teams and roles [see here](https://www.tines.com/docs/admin/teams)**

## Is there a way to get the event ID as it is emitted?

### List event

You can run the `list-event` API endpoint immediately after the action as shown [here](https://www.tines.com/api/stories/actions/list-events/).

The last event ID would relate to the one running.

### Metadata

The META key exposes a useful set of information about the current environment. View the full data structure it generates [here](https://www.tines.com/docs/formulas/referencing-data/#metadata).

To get the event ID, you can use:

`META.event.id`

> To learn more about the structure of Tines events, click [here](https://www.tines.com/docs/events)

## How do I update the results of an event payload for a previous action in a story?

In general, we do not edit or modify events once they have been emitted. However, an action with the same name can overwrite an event.

In the example below,

-   An alert is given a severity.
    
-   A set of conditions are checked.
    
-   The event is updated with a new severity.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2303259798/07d435e4599d2899b501ef4f42c3/d5a5521e-146f-4c36-aeed-36c43b487dbb?expires=1784786400&signature=39e61a63df7be31c99279aef1746ff90f91b6365f493c8b4ff7a0b121713f379&req=diMnFct7lIZWUfMW3nq%2BgZ%2BdicXeFRgUMhmqf5tqdCOZEeRnb6cRq7mfD2xX%0AXXwdVUKYDAv%2FhPsEXowViUKacm0%3D%0A)

## Does remitting an event count towards your event limit?

Remitting an event in Tines will count towards your permitted monthly event limits.

> -   To learn more about Tines features and pricing, click [here](https://explained.tines.com/en/articles/7007597-understanding-tines-pricing-plans).
>     
> -   Read about event limits in this [article](https://explained.tines.com/en/articles/12688669-what-s-an-event-in-tines).

## Event transform action FAQs

## Is there a way to emit more than sixty events per minute using throttle?

Sixty events per minute is the maximum that a throttle can emit.

> Learn more about throttle [here](https://www.tines.com/docs/actions/types/event-transformation/throttle).

## How can I reset an event transform action in deduplicate mode?

You can reset the event transform action in [deduplicate mode](https://www.tines.com/docs/actions/types/event-transformation/deduplicate) and clear the look back cache by clicking on the "status" tab and clicking on the red button as shown below.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2253001122/54094519074d0acccd19dc322ab3/CleanShot%2B2023-04-24%2Bat%2B15_29_54-402x.png?expires=1784851200&signature=17f41b8912feeb3be0b7594fe91bf4b46cce899e37778ba3d0a93d5da9c876b0&req=diIiFcl%2BnIBdW%2FMW3nq%2BgW4wtbpQQ5HfgWXGxjDUbfZjqXTB1JurRx0oNFp1%0A57ZBV9hTl3g%2FzrM3GvVapES%2B1%2FQ%3D%0A)

## Is there a maximum value for the delay mode of event transform?

There is no maximum value for the event transformation delay mode. If the previous event has expired and been deleted, delays will not work.

> For more details on delay mode see [here](https://www.tines.com/docs/actions/types/event-transformation/delay).

## Exabeam Authentication Guide

**Exabeam** is a global cybersecurity leader and creator of New-Scale SIEM™️ that helps organizations detect threats, defend against cyberattacks, and defeat adversaries, offering a new way for security teams to approach threat detection, investigation, and response (TDIR).

## First, get your Exabeam API Key

1.  Navigate to "Settings > Core > Admin Operations > Cluster Authentication Token"
    
2.  Click on the "+" icon  
    ​
    
    ![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/771361626/9179719e34effe6375f30ac8/RSb-TGIvL_wDClCwXF0RoUuZvVfbIMzcsOnf1cdUl1YcSQ0MYgFv0aUrTjaY9AI4l6q52WVbNLmSgtW4iwYOfDrkkJ1UBKSxMR2YiSGirYJR2OgrqrXP8xys_zpJ7qWzyngD5NZnDeGbnl2gP-vtahg?expires=1784851200&signature=182c76e682127d0a5c5d2917455bd131ea64f68ca21e8b627e881a5ad9465f75&req=cycmFc9%2Fm4NZFb4V1XW4gf%2FlrmjKYTNrArdPt5JL36Yi7Wai7GVyXSjMy26M%0AQSaNthS%2FIz%2BNzEKt78pJS05zLw%3D%3D%0A)
    
3.  Fill out the "Token Name" an "Expiry Date", select the "Default Roles" and then click "Add token"  
      
    ​
    
    ![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/771361632/e96bbf7d086a5bfdbd33026c/dOakp84u1rqKbO9XnB68Xr55kM3D8i6Mr8NnRLOuGVNY-opqjZeFXrMI6LsUezVX0Vyuat1ZCM-NjGHgCu-G7aeBYMGfj7VHPHin_2cnsvstPlHxLtptyn-w0vevLXUJXBZvF2JvaNWx8ERzdBwgvS4?expires=1784851200&signature=3eea8e778d91052fb7dc466947d2cbd313aca37992cf0e33ccd45dd8693b46ca&req=cycmFc9%2Fm4JdFb4V1XW4ge0Gd2QLNkRRtLe7zUKwFbzLnHDaH4plOx9T6m%2Fc%0Azrmq7ncbyc9bejbDFD8ZTinFog%3D%3D%0A)
    
4.  Select "Add New API Client"
    
5.  Enter details about the API Client, select the relevant scopes, then click "Add"
    
6.  Copy the "API Client ID" and "Secret"
    

## Lastly, Create an Exabeam credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "HTTP Request"
    
4.  Input the values for the Exabeam credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://api.us-west.exabeam.cloud/auth/v1/token/`
        
        1.  The Base URL may be different depending on where you account is based.
            
    
    1.  **Content Type:** JSON
        
    2.  **Method:** post
        
    3.  **Payload:** `{"client_id": "API Key","client_secret": "API Key Secret","grant_type": "client_credentials"}`
        
    4.  **Headers:** `{"accept": "application/json"}`
        
    5.  Click "Run options" and double-click on the key named `access_token` in the response.
        
    6.  **Location of token from response:** Paste the value copied in the previous step.
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**

## Using the credential in an action

When you make an API request, include the HTTP request type credential in the `Authorization` header after "Bearer" like:  
​

```
Bearer <<CREDENTIAL.exabeam_token>>
```

## Example approach: API pagination

## Overview

When communicating with APIs, you may receive a response that doesn't include all the results you expected. API pagination is a way to break large amounts of retrieved data into smaller, more manageable chunks. Compare this to online shopping, where you click "next" each time you want to view more items.

This article explains how to dynamically manage API pagination in Tines, ensuring you retrieve all the data you need.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - HTTP Request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Condition action](https://www.tines.com/docs/actions/types/condition/)
    

> **Tines tip:** Import our [Implement pagination with these techniques](https://www.tines.com/library/stories/91375) story into your tenant to help visualize how pagination can work in your story flow.

## Make it happen

### Identify pagination data

API pagination often involves looping through multiple pages of data. So first, we need to determine if the API endpoint we're accessing has additional pages.

In this video, we reach out to the Pokémon API endpoint and verify if it has more results than what's provided in the first page (i.e., the first response).

[Watch video](https://www.youtube.com/embed/K2gNymbN1io?rel=0)

In the API response (via the HTTP Request action's events), we can see that there is a `next` value with a URL. This URL indicates there are more pages of data:

![An event data payload from the Pokémon API showing the first page of results. The response shows count: 1302, with the next value highlighted in a red border containing a URL to the second page (offset=20&limit=20) and previous set to null, indicating this is the first page of results.](https://downloads.intercomcdn.com/i/o/am0g137l/1256643618/80d5b8e4656501c7d78eea016a80/image.png?expires=1784851200&signature=116596437d34fc54b76d85739c9a5ac6ea690cc465c5a292ff86996b5fa407f1&req=dSIiEM96nodeUfMW3nq%2BgbTRMbwXm1rMX2yZxaxCuwUvTMcEljuFpsmv82vy%0Afi8Qi2xM%2ByL6iJBPFbC9E5mW%2B3M%3D%0A)

### Dynamically call an API endpoint

While looping, we need our pagination HTTP Requests to be dynamic. This means adjusting them based on the current state of the loop, so that we're always pulling the correct data from the correct page.

In this video, we look at how referencing the `next` value of the upstream Get data action works to dynamically pull the next page of the API endpoint.

[Watch video](https://www.youtube.com/embed/IuHrgnD2eSk?rel=0)

> **Note:**
>
> -   When creating a pagination loop in Tines, we retain the same name for the actions that run the HTTP Requests. When we build our loop later on, we want to have our Condition action dynamically use whatever value is at the `get_data.body.next` path.
>     
> -   Other platforms may provide pagination values in their API responses differently than the example in this article. We always recommend checking your endpoint's documentation for more information.

In the output of the second HTTP Request action (the "pagination" action), we can see in its events a `next` value (showing there's more data to pull) and a `previous` value (showing that there were results pulled before this request).

![A Tines storyboard showing two HTTP Request actions both named ](https://downloads.intercomcdn.com/i/o/am0g137l/1256660178/83937e4efc87c84f9a47858ad08d/image.png?expires=1784851200&signature=7abc343d34ce8489e1f6acd495126978b8434622d71ceb13900f16a8cfba9306&req=dSIiEM94nYBYUfMW3nq%2BgQpkq1uz41hrzyRtH8KjFrd67j8AcyYrK6VWIyzW%0AFSiPJhD8UzyiUhcZsp48o7e69PE%3D%0A)

### Validate a pagination loop

To ensure our pagination loop is functioning correctly, we add a Condition action that validates the loop's progression, acting as a checkpoint to confirm that the loop is moving forward and pulling in the correct data.

In this video, we set up a Condition action to check if the `next` object of the Pokémon API endpoint returns a non-null value.

*Note: In this video, you'll see the action referred to as a "Trigger." This action has since been renamed to "Condition action." The functionality is the same.*

[Watch video](https://www.youtube.com/embed/IptVk_0t34k?rel=0)

With the Condition action in place, we can now loop the data, having the flow iterate to the next page.

![A Tines storyboard showing the Condition action ](https://downloads.intercomcdn.com/i/o/am0g137l/2179707469/a4d1c7997de1c0619803ead7684d/CleanShot+2026-03-19+at+15_01_58%402x.png?expires=1784851200&signature=00396a03497bcc821a6b06f0f5f4e86aa9721e6d2cf0a05bf48b1dfc59368f85&req=diEgH85%2BmoVZUPMW3nq%2BgckNw9St7LlygBQO7iiey%2BqxXwma17oEpyNv%2BkNL%0AmwcTCW5YduruUTb0nbjQGQs9g7s%3D%0A)

### End a pagination loop

All loops must come to an end. We can use the Condition action's "no match" flow to handle this, essentially defining what the story should do when `next` returns a null value.

In this video, we connect an Event Transform action to act as the end of the loop when the Condition doesn't match.

[Watch video](https://www.youtube.com/embed/UJUgO7d9Q7Q?rel=0)

Once `next` returns a null value, the story flow will go to the "Loop done" action, closing out the pagination loop.

![A Tines storyboard showing a pagination loop pattern. An HTTP Request action named ](https://downloads.intercomcdn.com/i/o/am0g137l/2179712300/e2c6178c7c6aeba932ef68e3c958/CleanShot+2026-03-19+at+15_03_11%402x.png?expires=1784851200&signature=d0565548c948566ef017641c1e39c9fa4bd47493f10cc05d8cd58de3d4967730&req=diEgH85%2Fn4JfWfMW3nq%2BgXtjcHuKq2broZV43VNQJOoBjm3aFG1Ed8SWfiVM%0Akl33ni1K3w8FZMy9xXeiKsWKGaA%3D%0A)

## Example approach: API rate limiting

## Overview

Imagine you're in a busy coffee shop, and the barista can only take a certain number of orders at a time to keep things running smoothly. If too many people place orders at once, the shop slows down or might even stop serving for a bit to catch up. This is similar to how **API rate limiting** works.

Rate limiting is the set of rules that control how many requests a user or system can make to an API within a specific time frame. By enforcing these limits, APIs prevent overloads, ensure fair usage, and keep the system working efficiently for everyone. In this article, we'll go over how to handle API rate limiting within your Tines stories.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - HTTP Request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Event Transform action - Explode mode](https://www.tines.com/docs/actions/types/event-transformation/explode/)
    
-   [Tines - Event Transform action - Delay mode](https://www.tines.com/docs/actions/types/event-transformation/delay/)
    
-   [Tines - Event Transform action - Throttle mode](https://www.tines.com/docs/actions/types/event-transformation/throttle/)
    

> **Note:** We always recommend reading your API platform's documentation for specifics on their rate limiting boundaries.

## Make it happen

### Leverage HTTP Request action options

By leveraging [HTTP Request action options](https://www.tines.com/docs/actions/types/http-request/#configuration-options) like `retry_on_status` and `retries`, you can handle rate limits gracefully. The `retry_on_status` option tells the action which HTTP status codes should trigger a retry. The `retries` option lets you customize the maximum number of retry attempts. Both are available by clicking **\+ Option** at the bottom of the action's config panel.

For example, if an API returns a `429` ("Too Many Requests") response, you can configure the HTTP Request action to automatically retry with exponential backoff by adding `429` to the `retry_on_status` array.

In this video, we show how to apply these two options for an HTTP Request action that returned a `429` response code.

[Watch video](https://www.youtube.com/embed/n-QeEjNjHXc?rel=0)

### Create dynamic delays

Sometimes it's necessary to [explode](https://www.tines.com/docs/actions/types/event-transformation/explode/) an array to feed individual values into an HTTP Request action, one at a time. When you do this, all the exploded events will try to process at once, which can trigger rate limits.

By adding a [delay](https://www.tines.com/docs/actions/types/event-transformation/delay/) after the explode, you can control the pace. The Event Transform action's delay mode accepts a `seconds` option that supports formulas, so you can create dynamic delays. For example, you can reference the Explode action's `index` value and use basic math to space out your requests (e.g., adding five seconds per index so that each event is delayed slightly longer than the one before it).

In this video, we look at an example of building a dynamic delay in a story.

[Watch video](https://www.youtube.com/embed/4N7W26reVQM?rel=0)

### Apply throttling via throttle mode

[Throttle mode](https://www.tines.com/docs/actions/types/event-transformation/throttle/) lets you control the rate at which events are emitted, regardless of how many events are queued up. For API rate limiting, this is useful when you want to set a fixed pace, for example, allowing only a certain number of events per minute to pass through to a downstream HTTP Request action.

You configure throttle mode by setting `runs_per_minute` and `events_per_run`. The throttle applies across all story runs, so even if multiple runs are queued, the rate stays consistent.

In this video, we look at an example of applying a throttle in a story.

[Watch video](https://www.youtube.com/embed/K5jPjo7JFfw?rel=0)

## Review the results

Tines gives you multiple options for managing API rate limiting. You can handle it reactively using `retry_on_status` to recover from rate limit errors, or proactively by using delays and throttles to control request pacing before hitting the limit. As you continue building, choosing the right approach (or combining them) will depend on the API you're working with and its specific rate limiting rules.

## Example approach: Data in, data out

## Overview

Whether you're just starting out or are an advanced builder in Tines, transforming your data throughout a story is vital to success. In this article, we cover some common methods using simple scenarios for restructuring event data within your stories to fit your needs.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - Event Transform action](https://www.tines.com/docs/actions/types/event-transformation/)
    
-   [Tines - Webhook action](https://www.tines.com/docs/actions/types/webhook/)
    
-   [Tines - Loops](https://www.tines.com/docs/actions/types/event-transformation/message-only/#looping)
    
-   [Tines - Tags](https://www.tines.com/docs/formulas/language/#tags)
    
-   [Tines - CAPITALIZE function](https://www.tines.com/docs/formulas/functions/capitalize/)
    
-   [Tines - INCLUDES function](https://www.tines.com/docs/formulas/functions/includes/)
    
-   [Tines - MAP function](https://www.tines.com/docs/formulas/functions/map/)
    
-   [Tines - UNIQ function](https://www.tines.com/docs/formulas/functions/uniq/)
    
-   [Tines - WHERE function](https://www.tines.com/docs/formulas/functions/where/)
    

## Make it happen

Both sections below use the following array:

```
{
  "items": [
    {
      "name": "Gala Apple",
      "type": "apple"
    },
    {
      "name": "Honeycrisp Apple",
      "type": "apple"
    },
    {
      "name": "Macintosh Apple",
      "type": "apple"
    },
    {
      "name": "Blueberry pie",
      "type": "pie"
    },
    {
      "name": "Raspberry pie",
      "type": "pie"
    },
    {
      "name": "Strawberry pie",
      "type": "pie"
    }
  ]
}
```

### Loops and tags

In this video, we look at how to handle an incoming array and modify it to meet specific criteria.

The criteria in this scenario is to:

-   Add "is delicious" after every `name` key value where "pie" is mentioned (i.e., "Blueberry pie is delicious")
    
-   Capitalize the `type` key value
    

[Watch video](https://www.youtube.com/embed/BOyy7Bnpc6g?rel=0)

We enable a loop in our Event Transform action to cycle through each object within the `items` array. We then use an `if` tag and the `INCLUDES` function to iterate through each looped `name` key value to check if "pie" exists. If it does, we add the "is delicious" string to it. If it doesn't, we leave the `name` key value as is. We also use the `CAPITALIZE` function to capitalize the `type` key value.

![An Event Transform action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1224121365/a8e78e02e404378e4aead200d390/image.png?expires=1784851200&signature=80489a8d285b4f0b0bd4417edc3506438529f49c687b323b43950967ff13e88e&req=dSIlEsh8nIJZXPMW3nq%2BgVT2v8UDQ0w23Da%2BdqeXk2GnMv0WPaze52EsZVAS%0AHybzuSCvNPPQQrKQPnb6XG8zvRg%3D%0A)

We **re-emit last event** from the "Catch Results" action and see the results of our pie formatting in the events of the "Format array" action.

![An Event Transform action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1224123571/c985f0f090312cdbd2a826737702/image.png?expires=1784851200&signature=ee291a09b3f7555329895340be3cb0311803c01de7201d304217e9284d77a822&req=dSIlEsh8noRYWPMW3nq%2BgT0sGcIzaL67Ub7qEtl9hJtzjgAlHvwLrQOFlsAq%0AxrVqKdue8x3I6VOc4zJg7nkpx%2B0%3D%0A)

### Organize event data

In this video, we look at how to take an incoming array and break it up based on a key identifier. In this example, we leverage the `type` key value of "apple" or "pie."

[Watch video](https://www.youtube.com/embed/Y42fM-a9qag?rel=0)

To create our categories (apple and pie), we use the explode mode of the Event Transform action. Since we only want to grab these specific values, we use the `MAP` and `UNIQ` functions to create a list array that includes one instance of each as the Explode **Path**.

![An Event Transform action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1224109237/fb7bc536ff943ab8f89a813d7d1e/image.png?expires=1784851200&signature=cac91a1fea7b2ca720ee3de68aa42eceb0c7e13a7bf1b868b007a4a1fe197350&req=dSIlEsh%2BlINcXvMW3nq%2BgafkwPUEzCHwoSIXs8SlVVpvhdTre2mKyN3OJhxJ%0AGXHn%2FtBCUxdQF6RakY4W2PUVZGw%3D%0A)

This creates an event for each category. Now that they're established, you can organize the original array. Using the `WHERE` function in a subsequent Event Transform action, we compare the original array's `type` key values against the categories we created in the Explode to see if there are any matches.

![An Event Transform action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1224116828/364fe40730b310466e91683105a0/image.png?expires=1784851200&signature=0d7ed878e790ef6d5f7ff23105f9bdef3da6d31d41d9b0589fbe54fe05410c41&req=dSIlEsh%2Fm4ldUfMW3nq%2Bgf0JbinMZe%2FQoLcBXJR6KSBzKm%2FV%2BOrWUxwlbU9r%0AhbWsRAjXWCItJaKnhuxGYp%2BOhpI%3D%0A)

After re-emitting from a previous event, we can see how the final Event Transform action is showing only the pie results.

![An Event Transform action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1224118067/48567957fcd05c29d19536fe84f6/image.png?expires=1784851200&signature=d1c89dba9d1f8d3021bb1c45bf6d21b23c552fec419612f9c7a21c56090b1930&req=dSIlEsh%2FlYFZXvMW3nq%2BgRaFnaUUwJ18LhoP9AEFOpYCJR1SWwrgSK2NsyyP%0AZdKTnEm0dONwPEArGESVAoPhlFo%3D%0A)

## Review the results

Mastering data transformation in Tines is key to building effective and efficient stories. By applying the methods outlined in this article, you can tailor your data structures to meet your specific requirements, improving the performance and clarity of your story flows.

## Example approach: Event timers

## Overview

As you build out your Tines stories, you may need user interaction mid-process for tasks like reviews, action validation, or confirmations. Combining the `PROMPT` function with Condition actions and delays is a useful pattern for these scenarios.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - Event Transform action: Deduplicate mode](https://www.tines.com/docs/actions/types/event-transformation/deduplicate/)
    
-   [Tines - Event Transform action: Delay mode](https://www.tines.com/docs/actions/types/event-transformation/delay/)
    
-   [Tines - Prompts](https://www.tines.com/docs/formulas/prompts/)
    
-   [Tines - PROMPT function](https://www.tines.com/docs/formulas/functions/prompt/)
    
-   [Tines - Condition action](https://www.tines.com/docs/actions/types/condition/)
    

## Make it happen

### Test a PROMPT

Prompts in Tines enable user interaction within your stories. Testing prompts ensures they function correctly, letting you verify user responses.

In this video, we show how to set up a `PROMPT` function within a Send Email action for a user to interact with (we emulate the interaction by pasting the `PROMPT` URL into a browser).

[Watch video](https://www.youtube.com/embed/Ipr0u8TRx7s?rel=0)

### Validate a PROMPT

Since the `PROMPT` function enables feedback loops, we recommend adding a [Condition action](https://www.tines.com/docs/actions/types/condition/) immediately after the prompt action to validate clicks. Without this, the story may continue unchecked.

In this video, we set up a Condition action to check if the `PROMPT` URL was clicked by the end user.

[Watch video](https://www.youtube.com/embed/BMxVR5Uy02U?rel=0)

### Remove duplicates from a PROMPT flow

To maintain the efficiency and accuracy of your story flow, it's important to remove duplicate submissions. [Deduplication](https://www.tines.com/docs/actions/types/event-transformation/deduplicate/) prevents repetitive actions and ensures that your automation only processes unique entries.

In this video, we walk through adding deduplication to a PROMPT flow.

[Watch video](https://www.youtube.com/embed/rCLEvo57m54?rel=0)

### Apply a delay with event timers

If the Condition action's criteria isn't met, we can add a [delay](https://www.tines.com/docs/actions/types/event-transformation/delay/) so the user has time to click the `PROMPT` link. In a real-world example, users aren't going to have zero seconds to click on something before action is taken.

In this video, we create a delay with seconds. You can always increase the delay as you develop stories for production.

[Watch video](https://www.youtube.com/embed/CxgzSUsl3j0?rel=0)

## Review the results

Whenever you have situational logic that relies on user interaction, it's good to do a final check.

In this video, we see what happens if the user clicks the link and how the delay event data flows through (or stops at) the deduplication action.

[Watch video](https://www.youtube.com/embed/VviamMm2fys?rel=0)

## Example approach: Refactoring stories

## Overview

One of the best practices to follow when building stories in Tines is refactoring. In this article, we cover some simple scenarios that showcase the most common methods of optimizing your story builds.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - Event Transform action](https://www.tines.com/docs/actions/types/event-transformation/)
    
-   [Tines - HTTP Request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Condition action](https://www.tines.com/docs/actions/types/condition/)
    
-   [Tines - DATE function](https://www.tines.com/docs/formulas/functions/date/)
    
-   [Tines - INCLUDES function](https://www.tines.com/docs/formulas/functions/includes/)
    

## Make it happen

### Collapsing multiple Event Transform actions

In this video, we look at an example with four Event Transform actions. The first three are using functions to capture different aspects of the date: day, month, and year. The final Event Transform is a reference of the upstream actions' values.

[Watch video](https://www.youtube.com/embed/Ex1xgDKNzPE?rel=0)

**Refactoring:** Instead of calculating each date piece in individual actions, we use that same series of functions in the fourth "Total Info" action. Once consolidated, we are left with the same end results of day, month, and year within one action, instead of across four.

![A Tines storyboard showing the refactored ](https://downloads.intercomcdn.com/i/o/am0g137l/1223875557/b6d3ed3689076abd5841ead47bc1/image.png?expires=1784851200&signature=8686cc9baf680594192c5e935661660d9122203f4e597757ad6a2995620753c7&req=dSIlFcF5mIRaXvMW3nq%2BgT3XcQlVDpDV5oYb5FkF7mAo8BpBZEAWvX3DDLM%2F%0AAZ4C0uQChAUDf44pv9tzGEIRR3I%3D%0A)

### Reducing actions with functions

In this video, we look at an existing story that uses Explode, Implode, and a Condition action to check an array of spells returned from an HTTP Request action and see if the "Feast" spell is included in the array.

*Note: In this video, you'll see the action referred to as a "Trigger." This action has since been renamed to "Condition action." The functionality is the same.*

[Watch video](https://www.youtube.com/embed/wrxaCQcG--c?rel=0)

**Refactoring:** Instead of building out this logic through multiple actions, this can be done using the `INCLUDES` function, which returns `TRUE` if the target includes the specified value. Wrapping the array in `TEXT` first makes it easier to check with `INCLUDES`. We build this into a Condition action to handle both `TRUE` and `FALSE` scenarios, if needed.

![A Tines storyboard showing the refactored ](https://downloads.intercomcdn.com/i/o/am0g137l/2179737496/5785d78b9ec187d0008d00242c5b/image.png?expires=1784851200&signature=ca8f510b6109b61dcc0b1e0a75541f1d916eac1a102f03b44dcf84a06e41a0d0&req=diEgH859moVWX%2FMW3nq%2BgdYytK3qPnyOJV%2FBZkV8qe1efZdEUd%2FHzrFZX21N%0ASm7rREGDyfIbAnfSV9h2IF0wRRk%3D%0A)

## Review the results

We optimize these stories by reducing the total count of actions on the storyboard. It's also easier to troubleshoot when all of the formula logic is within one action.

## Example approach: Story forks

## Overview

While building in Tines, you'll find that there are many ways to structure a story beyond a linear flow. A story fork is a design pattern where multiple streams of actions branch out from a single "kickoff" action, process in parallel, and then come back together to continue as a singular flow. Story forks are useful both from a processing perspective (running tasks simultaneously) and a visual perspective (keeping your storyboard organized).

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - Stories](https://www.tines.com/docs/stories/)
    
-   [Tines - HTTP Request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Event Transform action: Implode mode](https://www.tines.com/docs/actions/types/event-transformation/implode/)
    

## Make it happen

### Map an HTTP Request

We prepare our HTTP Request by identifying the URL, selecting the appropriate method (GET, POST, etc.), and specifying any necessary parameters. Let's look at an example using an API that doesn't require authentication (also known as an open API).

The Dungeons and Dragons 5e API lets us request information about spells, classes, and monsters. You can ask for the entire list of spells or retrieve a specific one like "acid-arrow." You can explore this API here: [https://www.dnd5eapi.co/](https://www.dnd5eapi.co/)

![The D&D 5e API homepage showing a request for a specific spell. The base URL field displays <a href=](https://downloads.intercomcdn.com/i/o/am0g137l/2241413188/80c071b71c9d07fafc6ce7c30064/image.png?expires=1784851200&signature=6190576429bcf61f99ba7e758feedcce3ffc8c8851e46d01ec74aa23eb753ddc&req=diIjF81%2FnoBXUfMW3nq%2BgQqBxL1pmoHB1zY41Z9ld5qrn444DWylrtSX9Ytc%0AjGkuJ27o3B9ytFlZOhNTe0GBQAo%3D%0A)https://www.dnd5eapi.co/api/2014/ with "spells/acid-arrow/" entered as the endpoint. Below, the JSON response shows the details for "Acid Arrow," including its description of a ranged spell attack that deals acid damage." width='848' height='834' style='height: auto;'/>

If you enter that URL into your browser, you'll receive the same information as you would from an HTTP Request action. In this example, we'll use [`https://www.dnd5eapi.co/api/spells`](https://www.dnd5eapi.co/api/spells).

![The D&D 5e API homepage showing the ](https://downloads.intercomcdn.com/i/o/am0g137l/2241414686/e15eba9c6929691c1ab8e6d72fef/image.png?expires=1784851200&signature=58048032bcda2e3527034d7faf0f3ad764e232c954551873172d1bc260377603&req=diIjF81%2FmYdXX%2FMW3nq%2BgUKsunaybNLeFzWR4AuzUXHQi%2FQ8%2BbSwOFOrrYFM%0Amr5vYSP18QyLWNqrFT8sci6W%2FTo%3D%0A)https://www.dnd5eapi.co/api/2014/ with "spells" entered as the endpoint. Below, the JSON response shows a count of 319 results, with the first entry being "Acid Arrow" at level 2 with the URL /api/2014/spells/acid-arrow." width='828' height='847' style='height: auto;'/>

To use this URL in an HTTP Request action, copy the URL and paste it into the URL field in the action's configuration. This approach applies to any API you want to call from an HTTP Request action.

In this video, we walk through setting up an HTTP Request action.

[Watch video](https://www.youtube.com/embed/8Q1nUlLrcb4?rel=0)

#### **​Example steps:**

1.  Add an HTTP Request action.
    
2.  Set the name.
    
3.  Set the URL. Retrieve this from the API's documentation.
    
4.  Set the content type to JSON.
    
5.  Set the method to GET. Most open APIs that don't require authentication use GET for retrieving data.
    
6.  Run the action and review the results in the event data.
    

This approach applies to any API you want to call from an HTTP Request action.

​

### Capture data in an Event Transform action

When you fork a story, you often need to capture and organize data as it flows through each branch. The Event Transform action lets you catch the incoming data and normalize it for consistent use downstream.

In this video, we walk through formatting event data with an Event Transform action.

[Watch video](https://www.youtube.com/embed/55v-84pDJv4?rel=0)

We use the naming convention "Format" to ensure that subsequent actions reference the `format.message` field. This approach lets you rely on the Event Transform action to normalize the data, so you don't need to worry about variations in the JSON paths across different HTTP Request actions that occur beforehand.

#### **Example steps:**

1.  Open the event data from the HTTP Request action you want to capture data from.
    
2.  Copy the path to the value you need.
    
3.  Add an Event Transform action and connect it to the HTTP Request action.
    
4.  Set the name to "Format."
    
5.  Paste the copied path into the message field.
    
6.  Repeat for the other branch.
    

> **Tines tip:** You can copy an action with `CMD+C` (or `Ctrl+C` on Windows) and paste it with `CMD+V` (or `Ctrl+V` on Windows). This saves time when you need the same action structure on multiple branches.

### Pull results together with implode

Now we need to aggregate the results from both branches back into a single event. This is where the [implode mode](https://www.tines.com/docs/actions/types/event-transformation/implode/) of the Event Transform action comes in.

Implode collects individual events and combines them into one. You configure it by specifying:

-   **Item path.** The data you want to collect from each incoming event. If each branch has an action named "Format" with a value in `message`, set the item path to `format.message`.
    
-   **Size.** The number of items to collect before emitting. Since this example has two branches, set the size to `2`.
    
-   **Identifier path.** How implode knows which events belong together. `STORY_RUN_GUID()` works well here because it generates a unique identifier for each story run, ensuring that only events from the same run are combined.
    

In this video, we walk through combining results with implode.

[Watch video](https://www.youtube.com/embed/Z5xlWVzxOYA?rel=0)

#### **Example steps:**

1.  Add an Event Transform action and connect both branches to it.
    
2.  Set the name.
    
3.  Set the mode to `implode`.
    
4.  Set the item path to the data you want to collect.
    
5.  Set the size to the number of branches (in this case, `2`).
    
6.  Set the identifier path to `STORY_RUN_GUID()`.
    

As an alternative to using an identifier path, you can add a `seconds` option via the **\+ Option** button at the bottom of the action. This tells implode to wait a set number of seconds after receiving the first event before emitting. If you use `seconds`, remove the identifier path.

![The Implode mode configuration panel for an Event Transform action, showing three fields: Item path set to format.message, Identifier path set to STORY\_RUN\_GUID(), and Size set to 2. A blue arrow points to the + Option button at the bottom of the panel. To the left, the + Option menu is open, displaying three available options: Customize output, Local values, and Seconds. The Seconds option is highlighted and described as ](https://downloads.intercomcdn.com/i/o/1168822519/ca6ac5e79ffd91482db10e16/b7104b05-c354-4ae8-87a3-df3295d86f77?expires=1784851200&signature=a455619762875280db5947818706bab34443ff2fbae779e7c8a8cfa08561bd88&req=dSEhHsF8n4ReUPMW3nq%2BgXPIeWZf1Xt3AxEGh9Wvp%2FMaFWWxFVu8BcCqKCpc%0AtFx%2B9SUWzdtnEBSemDk9QnLtARE%3D%0A)

## Review the results

Run the story from the top and watch how the two branches process in parallel before the Implode action combines their results into a single event. This pattern is useful any time you need to gather data from multiple sources simultaneously and then continue processing it as one unit.

## Example approach: Story troubleshooting

## Overview

While working in Tines, you may encounter situations that require troubleshooting. This article outlines a scenario that uses common methods to help resolve issues and ensure your stories function correctly.

## Tines references

Before getting started, we recommend familiarizing yourself with the functionality this article covers:

-   [Tines - Pages](https://www.tines.com/docs/pages/)
    
-   [Tines - HTTP Request action](https://www.tines.com/docs/actions/types/http-request/)
    

In this video, we walk through a story that experiences different errors. We also cover each of these topics at a high level throughout this article.

[Watch video](https://www.youtube.com/embed/nCPrsYNRIgY?rel=0)

## Make it happen

### Page behavior

In Pages, the **Page behavior** setting defines what happens after your **Button** is clicked. It can navigate to the next page, redirect to a URL, or display a success message. By default, it's set to **Show success message**. So if you want it to lead to another page, be sure to update this setting accordingly.

![A Page action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1223732281/aa7e2f4b5f4e7a36fc47d5b2c4f7/image.png?expires=1784851200&signature=bef45e7892adc1939f73846dd410a7101b741a30151b3901599f15b7b33b1e08&req=dSIlFc59n4NXWPMW3nq%2BgQUJiSU61iVrQtE7FAuRuSmnfhBVeNReLgEArIuW%0AH0YO1IkLV5rcp6FQ8dz3hd%2BbL%2Fc%3D%0A)

### HTTP request error

When making a request to a service, it's important to verify that the request is successful before proceeding with your story. After configuring your actions, always run them to ensure you're receiving the expected data. You can view more details in the **Logs** tab on the top right corner of the properties panel for a given action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1223713801/b946d9b1863855a7d00635cb5123/image.png?expires=1784851200&signature=c2ffb6bf2e23c9214f6b15eabcf52fd9c168ab28dcc7354863ff0e7d07d063a2&req=dSIlFc5%2FnolfWPMW3nq%2Bgevqsz0zNKneb7ZDty7NOQS807hc9nR%2BO2I%2Bpxqs%0AX8%2Bvyoldz1jgt6rGbWZnM%2BzZfVU%3D%0A)

In the example provided, we get a `404` response status code. The **Logs** of the action tell us that it cannot `POST`. This means that we are more than likely using the incorrect method in our API call:

```
Failed with 404 status code

Response body:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /api/v2/pokemon/120</pre>
</body>
</html>
```

We change the **Method** option in our HTTP Request action from `POST` to `GET`. This returns a successful response. If you still get an error, it's always good to refer back to the documentation of the API you're reaching out to.

### Re-emit last event

Instead of using the **Run** option on your actions, we recommend navigating to the last successfully executed action, clicking the three-dot menu in the bottom bar, and selecting **Re-emit last event**. This lets you quickly verify whether an updated or modified action works as expected.

![An HTTP Request action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1223749273/aed9f3e97652a353b4ff13e763bd/image.png?expires=1784851200&signature=86cf58743abb0d66ad03ee259b724dcaa13bd147eab9a7545b3edcedb6aa2b40&req=dSIlFc56lINYWvMW3nq%2BgbxFa8PPQtNZa3DJ4NUjnFLNfKU0p2mWFY6D9ks6%0AtMcJvqhjy994t9a1sVtsL0qKwjA%3D%0A)

### Incomplete path error

After re-emitting the last event from the top, we see that the final Page action has the following error in the **Logs** tab:

```
Incomplete path for value: `get_random_image.body.sprites.front_default.`
```

This means there was an extra period (`.`) at the end of the JSON path. We can also see this in the error itself. We'll fix this in the next section.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1223768834/ba731d559f98190015974628d4ec/image.png?expires=1784851200&signature=a0047958bb3a0cf703e4f18fb7c5c082ee3a867004aef69f6a4f8cdde0ed36e7&req=dSIlFc54lYlcXfMW3nq%2BgXES%2BJwszZS8AW8sZR4qcTTkHoQuYTM8t5bs6hFA%0AqLxW3TXtw%2FLYV9g7W1M8pucKZ80%3D%0A)

### Edit page

Since this is a Page we're working with, we click the **Edit** option.

![A Page action named ](https://downloads.intercomcdn.com/i/o/am0g137l/1223808289/ae4cfb5574ade52c10cef4a2d5ff/image.png?expires=1784851200&signature=34403853b1764be160bae2879743d8bd1fe7af432eeab951bb36a51a7ea86345&req=dSIlFcF%2BlYNXUPMW3nq%2Bgb4r2XvxPd1XIMNzTPjztPz9ClOa1jKThmmD6ied%0A6xu%2BDwXf8ke0NZtWw30qL2wnbc8%3D%0A)

This opens the Page configuration and lets us access its **Elements**. In this example, we're using an **Image**.

We click on where it says **Couldn't load image**. Here, we can see in the editor **Path** the incomplete path error from before.

Once that extra period (`.`) is removed, the results show what would be in that JSON path (since we fixed the previous HTTP Request action).

![The Page editor showing the Image element configuration after fixing the incomplete path error. The formula field at the top is highlighted, showing the corrected path get\_random\_image.body.sprites.front\_default (without the trailing period). The Result field is highlighted, showing a successful URL pointing to a Pokémon sprite image on GitHub. Below, the Page preview still shows ](https://downloads.intercomcdn.com/i/o/am0g137l/1223803821/baa7e1ce4fbef5296a3266d1cb8a/image.png?expires=1784851200&signature=b8233f6f2b33a39339f3f6971e1251eb360831269c80aea00c4021f015c9a680&req=dSIlFcF%2BnoldWPMW3nq%2Bgfywrrqic0ZOeRwP64mtjagkejeDMl%2BGrzmdw2gy%0Ahk4jsKgs4AQ6et3%2FtJlD4SOrH74%3D%0A)

## Review the results

After working through these issues, we run the story one last time to make sure it works as intended. Navigate to the first Page action as an end user, click Submit, and see the image results.

![The final results Page titled ](https://downloads.intercomcdn.com/i/o/am0g137l/2244981403/fefe03c6cec7ae9dc1709c0b70e5/image.png?expires=1784851200&signature=c6e38743950d3da86b9f7fcc75c9f967b37af330ebb476b96ca85c64720e49f4&req=diIjEsB2nIVfWvMW3nq%2BgYSYhY9rtP2fuBYPoQsEeNXeG4H5MaM0YAsUsKfd%0ANQLJ7pmUilPbsYcmvZo8ZRJw3vc%3D%0A)

## ExaVault Authentication Guide

ExaVault gives you a simple, reliable, and secure way to share files with anyone -- no matter what platform they use.

### First, get a ExaVault API key

1.  Login to your ExaVault account at [https://www.exavault.com/](https://www.exavault.com/)
    
2.  From the "Developers", select "API Keys"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852353816/43ec96ead07786003bbd5be3/image.png?expires=1784851200&signature=4081df2c6cdd909bd4a00fadb6adae61c339aa0864f63db210a550cd988fdd5c&req=fCUlFcx9lYBZFb4V1XW4geqFKyYzikjXuOFJAUNyggF0aEBmb44z8NCHChOa%0AwVt5wjf1Sv5XaEFFuAtnSROjQw%3D%3D%0A)
    
      
    ​
    
3.  Click "Add API Key"
    
4.  Name your key, enter your password and click "Save"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852354314/942e75cfb610e4078f188d47/image.png?expires=1784851200&signature=29b925c4afac6a15cfae6e879b377abac2b1f47366c7ca773c12ece4426a2787&req=fCUlFcx6noBbFb4V1XW4gakpzXhayQg728J%2FKL4JF8VhZlqVSYYfaxN%2BjSwQ%0A0clEfdH5aUYid0hkqVI9AYVjNg%3D%3D%0A)
    
      
    ​
    
5.  Copy your API key
    

### Lastly, create a ExaVault credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the ExaVault credential
    
    1.  **Name:** ExaVault
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of ExaVault** **stories in the [story library](https://www.tines.com/library?s=exavault&view=all)**

## Exporting Dashboards

In Tines, you can export both individual dashboard items or your entire dashboard. This article guides you on how to do this on either a one-time or recurring basis.

Read our introduction to dashboards [here](https://explained.tines.com/en/articles/9521649-introduction-to-dashboards).

## Exporting

### Individual graph

To export an individual dashboard item, click on the three dot options button on your graph and click ‘Export as image’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203633012/f4d6ada558e293775b933468ff83/dashboard+-+export+single.gif?expires=1784851200&signature=84b8cb2badff2eaff5be0611c3491b8ae8443c25dcd5b88975a147fada29141a&req=diInFc99noFeW%2FMW3nq%2BgVvAlJMm6YhzpwA3TZW9KnToqQP4aKMOJffumH%2Fv%0AUxNFqAFZl8GT64JBj8m4bRm9MmQ%3D%0A)

### Entire dashboard

To export an entire dashboard, click on the three dot options button at the top of your dashboard and click ‘Export as image’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203633982/e2d9859a706fdd7feb277d876002/export+all+dashboard.gif?expires=1784851200&signature=0775c0992ff51aaac5980dde3eb26a1d75d5ef187ddd72f5a91ea707f3139371&req=diInFc99nohXW%2FMW3nq%2BgVEUA91vAEEvP4UnDNjX0fgzEutI4Au1WwDedD7r%0AeDVxJ3g1VOj4CYEbcCBw%2BeUDHX4%3D%0A)

### Export options

Once you select your item for export, you can apply your preferences.

Preference options include:

-   Graph background color
    
-   Light or dark mode
    
-   Graph size options
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203631298/5e0a885d8da002827f3a153fef62/c2144361-c4de-431e-94b7-5ee2286cc6f5?expires=1784851200&signature=12df947319b69045c2a80e29af476841e797fd872ecc92c3ba2e5b33cfdd6472&req=diInFc99nINWUfMW3nq%2BgXpY1FiR0ZmamkIgDFUGFUA5oLWOY9kvrOJxlfB0%0As%2FZBIPBZV7Rko6vDOLbJSrOBHkk%3D%0A)

## Snapshot

You can also email a PDF snapshot of your dashboard on a daily or weekly basis to a list of emails.

In the toolbar at the bottom of your dashboard, click on ‘Snapshots’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203631300/d9537f0baf0268e729c7f4bd00c4/f99d80ba-9f22-425f-86a3-96d50cb602eb?expires=1784851200&signature=96629e8feed2ad7b9829047a24e4836ea15c742c938743a1de8c0b85f3faae7d&req=diInFc99nIJfWfMW3nq%2Bgcl27SLxvpmB8sGkhmboJDeKOvmyEpuJD9wykq0x%0AiGeISDBQl55fZJ2zZd0Aa2hEKr4%3D%0A)

Give your snapshot a name and add your recipients.

> Note: If you are adding multiple recipients, separate the email addresses with a comma.

Set how often and when you want your snapshot to run.

Finally, select which charts to include in your snapshot.

You can preview your snapshot before saving.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203637219/4a06523b40df68bbbd59ee488392/Snapshot+settings.gif?expires=1784851200&signature=9970a37d5b395ee11919d7f232c28d7e44a0addb154e9678dcbef8f1ef659e08&req=diInFc99moNeUPMW3nq%2BgboC8eOg9YungfQ8294f1%2BaNLLn73QinegIrvkMP%0AqbuOU506YQMoVabyyMkn4fGPSqg%3D%0A)

Learn more about dashboards in our [docs](https://www.tines.com/docs/records-cases/dashboards).

## ExtraHop Authentication Guide

### First, configure account in ExtraHop

1.  Ensure your user account has the appropriate privileges set for the type of task you need to perform (See [ExtraHop privilege levels](https://docs.extrahop.com/9.3/rest-api-guide/#privilege-levels))
    

### Next, create resource and credentials in Tines

One resource and two credentials will need to be created:

-   Resource: "extrahop\_username"
    
-   Text type credential: "extrahop\_secret"
    
-   HTTP request type credential: "ExtraHop"
    

##### Resource

1.  Navigate to the team that will be using the API and click "Resource"
    
2.  Click "+ New Resource"
    
3.  Input the values for the ExtraHop resource
    
    1.  **Name:** extrahop\_username
        
    2.  **Description:** Optional
        
    3.  **Builder:** your extrahop username
        
4.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
5.  Click "Save resource"
    

##### Text type

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the ExtraHop credential
    
    1.  **Name:** extrahop\_secret
        
    2.  **Description:** Optional
        
    3.  **Value:** your secret
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the ExtraHop credential
    
    1.  **Name:** ExtraHop
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<your_ExtraHop_domain>.api.cloud.extrahop.com/oauth2/token`
        
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:  
          
        ​
        
        ```
        {
        "grant_type": "client_credentials"
        }
        ```
        
    7.  **Headers:**
        
        1.  **Header:** Authorization
            
        2.  **Value:** Basic <<BASE64\_ENCODE(LOCAL.creds)>>
            
    8.  **Local values:** Click the "+ Option" button, select "Local values" and paste the below into the "Plain code" section:  
          
        ​
        
        ```
        {
          "creds": "<<RESOURCE.extrahop_username>>:<<CREDENTIAL.extrahop_secret>>"
        }
        ```
        
    9.  Click "Run options" and make sure you get a successful response.
        
    10.  **Location of token from response:** `<<extrahop.body.access_token>>`
         
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/816806186/b6033d12f007ec86576416f6/image.png?expires=1784851200&signature=36013dc43e60c2ba9bc26829cffbe5c6ed1561276292858c4da3525b8603a8da&req=fCEhHsl4nIlZFb4V1XW4gaSUYDkpo%2F8ZXM%2BBztzefIh7JBB4tN08SihPrPr5%0A3yzWKCqy2dJUdFpx%2BYTy0uyHFQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/816806323/df0ad41f5bbe020b2c5869ab/image.png?expires=1784851200&signature=f6ed2ff5f33a2d16ab100160b6a89828963b024fb5ac22b653dc32ab37e811ec&req=fCEhHsl4noNcFb4V1XW4gawJEnu%2B2iTa%2FDCWfCU0DBOnUEtHl9PNM0taG85F%0A4YUdD2HzQRWCEBWqRBSKkgxPXQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/816806584/f2def15cceec9b19a1e562c9/image.png?expires=1784851200&signature=bd02c0605aeeba3ca80510d41eb2761e1e400cdb8ff8fd3e5769f3659fcd4761&req=fCEhHsl4mIlbFb4V1XW4gYKiXwdvW86FIf9qTXQAOj7QRE4cufQ338u%2Fcvaz%0Ag1pBX6yi53yMO1vO9z56KuWiJA%3D%3D%0A)

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of ExtraHop stories in the [story library](https://www.tines.com/story-library?s=extrahop).**

### Using the credential and resources in an action

The Header configuration for your ExtraHop credential should be:

```
"Authorization": "Bearer <<CREDENTIAL.extrahop>>"
```

Here is an example ExtraHop action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"List All Detections in Extrahop","description":"List All Detections in Extrahop","options":"{\"url\":\"https://<<RESOURCE.extrahop_domain>>/api/v1/detections\",\"content_type\":\"application_json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.extrahop>>\"}}","position":{"x":690,"y":510},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"c8388b626850667f74b5b65c1146a8fe288572c5ffe5fdc11db00ebfff2a1866","createdFromTemplateVersion":1,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```

## Feedly Authentication Guide

Feedly is a robust feed reader that aggregates information from around the web into one convenient place. You can use it **to view updates on any topic you're interested in**, whether they come from the news, blogs, social media posts, RSS feeds, or other sources.

### First, get a Feedly API key

-   Login to your Feedly account
    
-   In the top left, select your workspace name, then under *Workspace*, select **Settings**
    
-   Within Workspace settings section, select the **API** tab
    
-   Select **New API Token**
    
-   Name your Token, and select **Generate API Token**
    

### Lastly, create a Feedly credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Feedly" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/feedly).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Feedly** **stories in the [story library](https://www.tines.com/library?s=feedly&view=all)**

## Fibery Authentication Guide

Fibery is a work management platform designed to help teams collaborate, organize information, and manage their workflows. It provides tools for project management, task tracking, and knowledge sharing. Fibery focuses on providing a flexible and customizable environment to accommodate various team structures and workflows.

## First, get a Fibery API key

1.  Login to your Fibery account
    
2.  Click on the user menu and then click "Settings"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948716295/69bfb2b248f9e036b8540bd5/image.png?expires=1784851200&signature=bc6fbf93186d5973ebe8f254807c98dedc261d0236683d54026b818b485c7c20&req=fSQvEch4n4haFb4V1XW4gZBkUGrktH71bg1w1IPecFcMZT80X0UNE%2FoHBh0z%0AKsVbibiNL6tHYh1xX%2FWWIZ7YGQ%3D%3D%0A)
    
      
    ​
    
3.  Click "API Keys" and then click "Generate API key"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948716594/4ba9d036397c5c9e4e0b41bb/image.png?expires=1784851200&signature=6e9775aa506f70d8b3aa29b470298d5f2566a1b702b58bc2ea0cbc1bf478a96f&req=fSQvEch4mIhbFb4V1XW4geM%2FIf2yPPw9Az8ZsBzBlN5%2F%2BnCqAsnzJiTtiOnC%0AnEIuCt2dEjUc%2B%2FNtYg79Prf33A%3D%3D%0A)
    
      
    ​
    
4.  Copy your API key to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948717282/d75cff2d0dd031a514fa8cd5/image.png?expires=1784851200&signature=376e436f25b00b4d4a937873fd886449220e580ee7a7d91c72da6bc643a8076b&req=fSQvEch5n4ldFb4V1XW4gXXX3MG0kknUxXWwJ6cLWjPRszxoRZhEWOZFZsr8%0AP%2FSQeexeBloN%2FkBnKnNGM9ne9A%3D%3D%0A)
    

## Lastly, create a Fibery credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Fibery credential
    
    1.  **Name:** Fibery
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Fibery** **stories in the [story library](https://www.tines.com/library?s=fibery&view=all)**

## Formulas and pills explained

Many automation tools claim to be no-code. But once you need to do something tricky, like precisely assembling a piece of text, or modifying the structure of data between APIs, you’ll find yourself blocked, or needing to write code. That's where formulas come in. If you’ve used tools like Excel, you likely already know how to use them. And with our built-in library of *hundreds* of functions, and easy-to-use formula “pills”, you can easily tap into the power you need. Here's how they work.

Just like in spreadsheets, formulas in Tines allow you to *transform* data.

Where a formula in a spreadsheet references data from other cells, a formula in Tines references data from various other places in Tines. Formulas in Tines let you reference:

1.  Upstream event data
    
2.  Resources
    
3.  Credentials
    
4.  [INFO](https://www.tines.com/docs/formulas/referencing-data#info) and [METADATA](https://www.tines.com/docs/formulas/referencing-data#metadata)
    

### **Referencing upstream actions with formulas**

Let's pretend we have an action that fetches the value from a form on page.

A downstream action could reference that data, for example, it could include the email address that the user submits. The formula would read something like:

`user_form.body.your_email_address`

### **Referencing resources with formulas**

Resources in Tines are reusable pieces of data. Things like the same text, object, array, and file can be used used multiple times across multiple stories. To reference a resource in a formula, you can use the special RESOURCE key. Let’s say we have a text resource named "Domain name", the following expression would fetch it:

*`RESOURCE.domain_name`*

### **Referencing credentials with formulas**

The CREDENTIALS key works identically to the previous RESOURCE example, for fetching credentials. **Note:** because these are inherently sensitive, we’ll never output a preview value in the formula builder when working with credentials. Use the following expression:

*`CREDENTIAL.example_api_key`*

### **Add formulas using “formula pills”**

Formulas are most frequently used in actions. You can add one to the content of actions in Tines using a formula *pill*. Think of pills like dynamic variables in action text. Pills allow you to reference and format incoming event data using formulas. There are a few kinds of formula pills;

-   Value pills
    
-   Tag pills
    

When you use a pill, it displays the result of the formula expression when the action runs. For example, “The total amount is **SUM(array)***”* would read “The total amount is **300**”.

Tags, on the other hand, control the flow of execution. Tags are particularly useful for doing things like building emails.

You can add a Value or Tag pill like this:

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/961980237/9babde3cb469fa10f32f8a6b/IZ9GA1qWvGNtf2KNHdvTZ4ZhI6wlffEXze2rC56p0JPaoWFbB5CVy1OthxE-UlJHExWYNY_XzR7cec2M0yvNh5fyxgYaE4ZKDwSR4plfpe0FpX4xPkNH5soLE_ns7TNBxx6OZc2IXcTWJ72BpFEmqck?expires=1784851200&signature=9cab1eca6400ada11862c13e9bd1ad5b0835b10192d457418e4e9e9b9b4b89b1&req=fSYmH8F%2Bn4JYFb4V1XW4gffbKT30TJ67HoGsnkP1ADbuvHROTmZfkQgE0RkL%0AJfzud94zpoNFT13utF1Fxh%2FrFw%3D%3D%0A)

### **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.

### **Adding formulas using “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, inside the builder window. 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.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/961980253/43466eb6e6d9d7c72e883d2e/-MzMxOFsUKKZV44WRiF94JOtZC21LJffEqaycrWbcCd8Us3fzPZYm6YkxH7Z4agcgoHdWbMiQdbzordOX_CTJ2tk3g-C3yOXWF2oq9oICvTMtOsVgBUSMQeO_1drMutys4lja4FpN9tYWJ6OIV40kdA?expires=1784851200&signature=427dd3e44b5eb6233d91dab73e2cf2991c515a0ec5a0b8e81aa505730fd49585&req=fSYmH8F%2Bn4RcFb4V1XW4gceE4ZpN%2FS%2BeULdDbr3mNCMy5DgWilh9bqB784vS%0AgyEXfZH%2BcxZGru%2FJRAdDMLLiWA%3D%3D%0A)

> To start building with formulas now, you should check out [these examples](https://www.tines.com/docs/formulas/examples), and our [library of functions](https://www.tines.com/docs/formulas/functions).

## Freshservice Authentication Guide

Freshservice is a cloud-based IT Help Desk and service management solution that enables organizations to simplify their IT operations. The solution offers features that include a ticketing system, self-service portal and knowledge-base.

## First, get a Freshservice API key

1.  Login to your Freshservice portal
    
2.  Click on the user menu and then click "Profile Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/969214998/5d342725eebf95b2d328f48b/image.png?expires=1784851200&signature=a6f2bb74d008390da9ce9207c8520772d099f8a8ff3049d9cadc65ccbd725121&req=fSYuFMh6lIhXFb4V1XW4gYh9h%2BK3OUlyjm01YZk%2Be90i%2FN%2F6nPs1QWV%2F6wVF%0AV1%2F9DZvCdQO40lYA8%2FGLmpnGTg%3D%3D%0A)
    
      
    ​
    
3.  Copy your API key
    
    ![](https://downloads.intercomcdn.com/i/o/969215448/ceb7205a8cc09bc3e1e6ea71/image.png?expires=1784851200&signature=7fda06931bbedcc69a5cf4eec103151edf74dda590485ba6243b2732d2637486&req=fSYuFMh7mYVXFb4V1XW4gQf%2FJhG6zCAimNtSnbBOFZfnkMWUzafvPWLhfK2y%0Aw4fmmHWN8ioM6ZZ5I8JozuaSOQ%3D%3D%0A)
    

## Lastly, create a Freshservice credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Freshservice" and follow the prompts to connect.  
    ​
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/freshservice).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Freshservice** **stories in the [story library](https://www.tines.com/library?s=freshservice&view=all)**

## Functions FAQ

A function is a reusable instruction that you can use to perform certain tasks in Tines. [This article](https://explained.tines.com/en/articles/9009911-functions-in-tines-explained) explains functions in Tines.

## Is there any performance difference between nesting function calls vs. using the pipeline?

There is no difference in performance when using nesting functions rather than pipelines.

Examples:

-   Nesting functions: `func2(func1())`
    
-   Pipeline: `func1() |&gt; func2(%)`
    

For more information on operators in Tines, [see here](https://www.tines.com/docs/formulas/operators).

## What type of regex does Tines use?

Tines uses Ruby-compatible regular expressions (regex).

> You can use [https://rubular.com/](https://rubular.com/) to test your regex as you build.

## How do I find which object in the array has the largest ID?

You can use [SORT()](https://www.tines.com/docs/formulas/functions/sort) to sort the array based on the key and then use [LAST()](https://www.tines.com/docs/formulas/functions/last) to select the last object in the sorted array (the highest value)

`SORT(example.data, "id") |> LAST(%)`

To learn more about working with arrays, click [here](https://www.tines.com/docs/formulas/functions#arrays).

## How can I remove unwanted elements from an array?

The REJECT function can remove unwanted elements from an array. This example removes empty strings from an array.

`REJECT(array, ["", NULL, 0, FALSE])`

Read about all of our functions in our docs [here](https://www.tines.com/docs/formulas/functions/).

## Functions in Tines explained

### What is a function?

A function is a reusable instruction that you can use to perform certain tasks in Tines. You can tell Tines to do a whole range of things using functions, such as edit or transform data. Functions can also tell you things *about* data, giving you values like counts, dates, times, and others. Functions are a critical component of what makes Tines so powerful - allowing you to automate specific tasks with any set of data.

The best thing about Functions in Tines? If you know how to use Functions in Excel, you already know how to use Functions in Tines.

### An example of a function

Each function has a name denoted in `CAPSLOCK`. A simple example of a function is the function `JOIN`.

Let's say you have a list of items and want to join them together in a single string, you would use the `JOIN` function. This function combines the elements in an array into a single text value. So, if you had an array that had four items in it, north, south, east and west, it might look like this:

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/976661162/c282fdf94b5f4b8867152e30/NAbEPBYM7PyWa5j0UV3Y8VDWgIZqvKqb16OIzHr_UDf-_PSH1Lf1UIswM9QzlDQgRLY_o2zVMTdQmxXMpJPSHj_sCmeBcEgZOeXo-4lqHzCYUOrqo4XOKMRE3QQX8qb7iIyEJAOb_ymvl6sSjA9yds4?expires=1784851200&signature=e032cc8a0a596e22563863f08f0db86c5761ec285af7713d99531f0a0acf15a9&req=fSchEM9%2FnIddFb4V1XW4gRcS9UKzJTU8mya2Awe%2BgcVVWIevmuj%2BHs5GBbRX%0AJkEqPLsIfJlMAgrmsVxTulqvPA%3D%3D%0A)

The `JOIN` function would join all those items together into a single string. The formula syntax for the `JOIN` function looks like this: `JOIN(array, join_text)`. To do this, you would add an Event Transform action containing that formula to your story.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/976661181/49f82fe099273f2e0d0b7f75/4K94xXJrSr5nZ7WNPtCYrv1uJa7WJnwQfrgeoMwvdrX6S4XrsA_iXB9-LvA6Lde5ffdTb3zWBHyEVqqt4zomO_Hsyb_f1K5YZgpkyZY3_wCzkisf_Y_-PBeXHoaLEfpMLHXJjSSH1XMG229XvtZYRls?expires=1784851200&signature=e51ba027cd67b973dcfd4ad71e975cd55ff6f286a11dfb9ce9f632f0348936e5&req=fSchEM9%2FnIleFb4V1XW4gbtpMPBbOrpfHhG2fma5n93K73smr4JzvXCjKxoI%0ARQpC55CZdDQDH%2BKImbl3%2FJK%2BEw%3D%3D%0A)

Then, in the `JOIN` event Transform action, enter `JOIN(my_action.message, “ ”)` in the payload of the build pane. When you run the story, the output would be "north south east west".

### Best practice advice from the Tines team for using Functions

Here at Tines we use functions *a lot*. So, to help you get up to speed with Functions as quickly as possible, we’re sharing our best practice advice with you.

### **First, let Eoin show you around**

Eoin Magner is a customer success Engineer at Tines. In this 3-minute video below he shares his top tips for getting started with functions in Tines.

[Watch video](https://www.youtube.com/embed/az_g2dxeXlc?rel=0)

### **Start simple**

A great place to start is our functions library, [here](https://www.tines.com/docs/formulas). But, it’s a very long list and is most useful if you know what you’re looking for. So here’s a short list of *just some* of the useful (but also easiest the get started with) functions to get quick results from:

-   **[SIZE](https://www.tines.com/docs/formulas/functions/size)**: This function returns the number of characters in text or the number of elements in an array. For example if you had an array with four elements, the `SIZE` function would return an output of 4.  
    ​
    
-   **[PLUS](https://www.tines.com/docs/formulas/functions/plus)**: This function adds a number to another number. For example, if you had the number 6 in a message, you could use the PLUS function to add 2 to it, with an output of 8.  
    ​
    
-   **[DAY](https://www.tines.com/docs/formulas/functions/day)**: The function `DAY` returns the day of the month for the specified date. The accepted Format is \`year-month-day (year/month/day)\`. So for example, if you use the accepted format (e.g., 2022-03-19) you would get an output of `19`.  
    ​
    
-   **[DOWNCASE](https://www.tines.com/docs/formulas/functions/downcase)**: This function turns any uppercase character in text to lowercase. For example, it would turn ‘Hello World’ into \`hello world\`.  
    ​
    
-   **[JOIN](https://www.tines.com/docs/formulas/functions/join)**: As we saw in the example above, this function combines the elements in an array into a single text value using the argument as a separator.  
    ​
    
-   **[SORT](https://www.tines.com/docs/formulas/functions/sort)**: This function sorts elements in an array by a property of an element in the array. For example if you had an array of users, and each had the following elements: First Name, Surname, Id, Job, you could use `SORT` to order the list of users by surname.  
    ​
    
-   **[DEFAULT](https://www.tines.com/docs/formulas/functions/default)**: This function allows you to specify a fallback in case a value doesn’t exist. For example, if you wanted to say Hi "first name" but we don't have a first name, we can say "hi there" where the fallback is "there"
    
-   **[INCLUDES](https://www.tines.com/docs/formulas/functions/includes)**: This function returns `TRUE` if the target (which can be text, an object, or an array) includes the value, otherwise it will return as `FALSE`. For example if you had a target with a value of “Hello world” and you used the formula `INCLUDES(my_action.value, "world")`, it would return an output of `TRUE`.
    

### **Make use of example blocks**

Our next piece of advice is to familiarize yourself with the **usage examples and in particular, the sample actions.** You can easily copy paste these from the functions library into your own story. It’s a serious time saver!

Copy these into your own story to see and understand how it works there and then. Now you’ve got a great starting point to input your own specifics in place of the example content.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/976661199/ad06ae010f002aa9f4938915/eU0mdtPo2-gDHymp7giAlz4i8vjzwLXaSDwVR_R8aTiaQPzvom1ZgmIi8vT-sp8oOHJStr6_SLbYkMUQY-aMBOjPwsweM4p0ZzBgj9oBg0-9Ezh7Ynz_hReF5gZidlKYPbW6d2cYmdz0AsQ0c3WA1IY?expires=1784851200&signature=8217ab1841339d6c8e279326be260f294b8b2c21b89ca245c849611e3d6d89f9&req=fSchEM9%2FnIhWFb4V1XW4gebPf1xS6qjkCaHCpcDJryM1UkjG2Xuh0AnSUrI5%0AA1yf1c%2Fxp2%2F4UjN4RhEJP6dm1Q%3D%3D%0A)

### **Leverage the highest-impact functions**

There are *hundreds* of predefined functions in Tines, but some are more valuable than others. Where some are highly specific, others provide greater utility. Below is our list of the most valuable ones. These can also be some of the trickier ones to get working, so we recommend starting simple with the list above in #1.

-   **[MAP](https://www.tines.com/docs/formulas/functions/map):** The `MAP` function creates an array by extracting the values of a named property from an array of objects.  
    ​
    
-   **[LAMBDA](https://www.tines.com/docs/formulas/functions/lambda):** The `LAMBDA` function is basically a custom function, where you can create your own function to perform a specific task. `LAMBDA` can be used as a standalone function or within other functions, such as `FILTER`, to perform calculations on each item in an array.  
    ​
    
-   **[MAP\_LAMBDA](https://www.tines.com/docs/formulas/functions/map-lambda):** Now that we know `MAP`, and now that we know `LAMBDA`, it’s easiest to think of `MAP_LAMBDA` as a combination of these two. Where `MAP` iterates over an array, and `LAMBDA` gives us a reusable function, `MAP_LAMBDA` iterates over the array and performs an operation defined in the `LAMBDA` on each entry.  
    ​
    
-   **[WHERE](https://www.tines.com/docs/formulas/functions/where):** This function will select all the elements in an array where the key has the given value.  
    ​
    
-   **[FILTER](https://www.tines.com/docs/formulas/functions/filter):** Filter is a function that takes either an array or an object, along with either a lambda function or an array of values to retain. When provided with an array, it returns a new array containing only the values that satisfy the condition specified by the lambda function or are included in the array of values to retain. When given an object, it returns a new object containing only the key-value pairs where the values satisfy the condition specified by the lambda function or are included in the array of values to retain.  
    ​
    
-   **[REJECT](https://www.tines.com/docs/formulas/functions/reject):** This function lets you remove specified objects, lists, or arrays. For example, if you had an array 1,2,3,4,5,6,7,8,9,10, you could use `REJECT(my_array, ARRAY(2, 4, 6, 8, 10))` which would give you an output of 1,3,5,7,9.  
    ​
    
-   **[REDUCE](https://www.tines.com/docs/formulas/functions/reduce):** This function turns an array into a single value. For example, if you had a list of numbers, you can add them all together, multiply them, or find the maximum number. For example, if you had an array of numbers, you could iterate through the array, adding on the current number to the total of all previously summed numbers.

## Generate a HAR file for troubleshooting

## Overview

When working with Tines Support, you may be asked to generate a HAR file to help with troubleshooting certain issues. In this article, we'll cover the steps on how to do this in commonly used web browsers.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   A **[HAR file](https://en.wikipedia.org/wiki/HAR_\(file_format\))** [(**HTTP Archive file**)](https://en.wikipedia.org/wiki/HAR_\(file_format\)) is a log of a browser's network activity, and is useful for capturing information behind-the-scenes for scenarios like UI performance and API issues.
    
-   Need to strip out sensitive information from your HAR file before sharing it with Tines Support? Cloudflare offers a [HAR sanitizer tool](https://har-sanitizer.pages.dev/) that can be used to redact this data.
    

> **Note:** A HAR file records and captures browser activity and can contain sensitive data such as cookies, passwords, client secrets, and other data. Please ensure that you remove any sensitive data and secure your HAR files accordingly.

## Make it happen

Jump to your browser's section to get started:

-   [Chrome](#h_5d53b8cde2)
    
-   [Edge](#h_03c2bcaf7b)
    
-   [Firefox](#h_1340a02345)
    
-   [Safari](#h_8c90c47169)
    

> **Tines Tip:** If you have any additional questions on how to generate a HAR file outside of this article, we recommend visiting your browser's documentation for more info.

### Chrome

1.  Open Chrome and navigate to the location in Tines where the behavior is occurring.
    
2.  Navigate to the **⋮** button → **More Tools** → **Developer Tools**. Alternatively, you can use the keyboard shortcut: `Ctrl + Shift + I` for Windows; `Cmd + Option + I` for Mac.
    
3.  In the pop-up panel, select the **Network** tab. **Note:** You must keep this tab open while reproducing the behavior.
    
4.  Click the grey **Ø** cancel icon to clear out existing logs.
    
5.  Check the box next to **Preserve log**.
    
6.  If the red **Record** button (●) is gray, click it to start recording. It should turn red when active.
    
7.  Reproduce the behavior.
    
8.  Once you've finished, click the Export button to save the HAR file to your computer.
    

Here is a Chrome screenshot of the options mentioned in the steps above:

![](https://downloads.intercomcdn.com/i/o/1171498780/973e387e200f4a739ed13d86/image.png?expires=1784851200&signature=824cb4b37e45ad08c44cbcb8f922b72d5c1d46f5fa323b13b875f0be7ba2435a&req=dSEgF813lYZXWfMW3nq%2BgYy382DgFcL3pM%2FfbKLq4BgKm1DXOOGgVyn5WEO2%0Ay5fXIczKhcbmW0cgVR0BRsT1Vfo%3D%0A)

### Edge

1.  Open Edge and go to the location in Tines where the behavior is occurring.
    
2.  Navigate to the **. . .** button → **More tools** → **Developer tools**. Alternatively, you can use the keyboard shortcut: `Ctrl + Shift + I` for Windows; `Cmd + Option + I` for Mac.
    
3.  In the pop-up panel, select the **ᯤ Network** tab. **Note:** You must keep this tab open while reproducing the behavior.
    
4.  Click the grey **Ø** cancel icon to clear out existing logs.
    
5.  Check the box next to **Preserve log**.
    
6.  If the red **Record** button (●) is gray, click it to start recording. It should turn red when active.
    
7.  Reproduce the behavior.
    
8.  Once you've finished, click the **↓** **Export HAR file** icon to save the HAR file to your computer.
    

Here is an Edge screenshot of the options mentioned in the steps above:

![](https://downloads.intercomcdn.com/i/o/1171500059/879c75e8ffe52982d47fb948/image.png?expires=1784851200&signature=f50fedefa292da2665aba68a5612926309cc2d655eb4c4a4e61e9515f0e14662&req=dSEgF8x%2BnYFaUPMW3nq%2BgV8HFfFrgO83U9meZU%2FKLtYWky4ERuYCykt6mPV0%0A1r55aSutdYi0bbf3IFUba6ZVPWM%3D%0A)

### Firefox

1.  Open Firefox and go to the location in Tines where the behavior is occurring.
    
2.  Navigate to the **☰** button → **More tools** → **Web Developer Tools**. Alternatively, you can use the keyboard shortcut: `Ctrl + Shift + I` for Windows; `Cmd + Option + I` for Mac.
    
3.  In the pop-up panel, select the **Network** tab. **Note:** You must keep this tab open while reproducing the behavior.
    
4.  Within this tab, click on the⚙️cog icon → **Persist Logs**.
    
5.  Reproduce the behavior.
    
6.  Once finished, navigate to the ⚙️cog icon → **Save All as HAR** to save the HAR file to your computer.
    

Here is a Firefox screenshot of the options mentioned in the steps above:

![](https://downloads.intercomcdn.com/i/o/1171519484/b4b0f871f7f59b9aa470b010/image.png?expires=1784851200&signature=fe3f411a8f26a7774ec05c6a13a06a6f563ef91db08abd1d7951b94e36d2d0ca&req=dSEgF8x%2FlIVXXfMW3nq%2BgZ2Y9vsH%2BMzy3st%2FjqbPKrYIA7E2WkrJ9GKLv8f6%0AfbDTFVQyRaHTKx%2FnPixtNRIOwZ8%3D%0A)

### Safari

1.  Open Safari and go to the location in Tines where the behavior is occurring.
    
2.  Navigate to the **Develop** menu. **Note:** If you don’t see the **Develop** menu, go to **Safari** → **Settings** → **Advanced**, then check the box next to “**Show Develop menu in menu bar**”.
    
3.  Click **Show Web Inspector**.
    
4.  Navigate to the **Network** tab. **Note:** You must keep this tab open while reproducing the behavior.
    
5.  Click the **ᯤ** icon → **Preserve Log**.
    
6.  Reproduce the behavior.
    
7.  Once you've finished, click **Export** to save the HAR file to your computer.
    

Here is a Safari screenshot of the options mentioned in the steps above:

![](https://downloads.intercomcdn.com/i/o/1171514947/7cc72b54aad1c10f087ca14a/screenshot_2024-09-06_at_2_57_11_pm.png?expires=1784851200&signature=d50e624835fe8043e3e2bfbfaac2d4a4063810e4b36c18c07a6d3c1a20030bcd&req=dSEgF8x%2FmYhbXvMW3nq%2BgZ5LJqjgybxoOk9JU0A0Z4n14UMbKwApbCQyjd0M%0AudVGRaFrvHQQj0G%2BSBt0jlWgsTs%3D%0A)

## Review the results

Once you've successfully generated a HAR file from your browser, you can upload it to your conversation with the Tines Support team to continue investigating issues.

## Get started with Agents

AI Agents in Tines give you the power of AI within your story. To use the AI Agent action, you need to have access to [Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench) *and* have [AI-powered features enabled](https://explained.tines.com/en/articles/9396326-how-do-i-turn-on-or-off-the-ai-powered-features).

> **Note:** Using the AI Agent action will use AI run-time credits. You can see your credits used to date by each team in the AI settings modal. Learn more about AI run-time credits [here](https://explained.tines.com/en/articles/9369092-ai-action-credits-executions).

To start, select the AI Agent action and drag it onto your storyboard. From there, you have two modes to choose from in the right-hand panel: Task and Chat.

1.  **Task** - Define and run a prompt within your story. Optionally, add tools to take action on your behalf.
    
2.  **Chat** - Create your own AI chat interface for audiences inside your organization.
    

This video looks at when you should use a Task, and when a Chat would be better suited to your story:

[Watch video](https://fast.wistia.net/embed/iframe/bbu629rptr)

### Customizing your agent

Like all actions, you can update the name and description in the right-hand panel. Additionally, the modes in the AI Agent action have specific configuration options:

#### Task

| **Setting** | **Description** |
| --- | --- |
| Prompt | The task the action should perform during this run, including any data it should use. |
| Tools | Optional. If you want the agent to take action, you should give it access to tools. Click on the tool in the AI Agent action on the storyboard or within the panel to configure it. Agents can only use the tools you give it access to. |

> Note: In task mode, the agent defaults to a cheaper AI model when no tools are added. Once a tool is added, it automatically switches to the smart model.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1583669713/1bebf9b588b50c577e62d8c6fc8e/CleanShot+2025-06-23+at+10_11_17%402x.png?expires=1784786400&signature=7fc4151d0812f72fca8d41c3245282c5a1efb1eb5e62cccfc53d5a9d8d01e78d&req=dSUvFc94lIZeWvMW3nq%2Bgb0XY7xbyh580I4ko3DOBYnERM9XfeW2w%2FuTwren%0Anw1fe53dcMhec0tniipidwoFgRo%3D%0A)

#### Chat

| **Setting** | **Description** |
| --- | --- |
| System instructions | The guidelines, rules, and constraints the AI model should follow. The task will be pre-configured with this instruction, which you should customize depending on the goal you're looking to achieve: You are a focused, expert assistant. You are talking to a user and aiming to achieve the following goal: {goal}​ |
| Initial message | The initial message to send to the user when the chat page is loaded. If this is left blank, the agent will create a message based on the instruction. |
| URL identifier | Defines the URL this chat page will be available at. Be cautious: changes could break bookmarks. |
| Tools | Optional. If you want the agent to take action, you should give it access to tools. Click on the tool in the AI Agent action on the storyboard or within the panel to configure it. Agents can only use the tools you give it access to. |

Chats use some of the same [customization options as Pages](https://www.tines.com/docs/pages/branding-and-style/). You can customize these to change the look and feel of the page, as well as the user experience of the page that will be sent to and seen by users.

| **Setting** | **Description** |
| --- | --- |
| Access control | Choose who should have access to this chat. You can choose between "Only team members" or "Members of this Tines tenant" |
| Appearance | Select light mode or dark mode for the chat. |
| Action color | This will be used to tint certain interactive elements like buttons. |
| Logo | Enable this to upload a logo for your page. This will appear at the top of the chat page. |

![](https://downloads.intercomcdn.com/i/o/am0g137l/1583670036/7aa63f99c51cd63bce0ef84717a4/CleanShot+2025-06-23+at+10_11_34%402x.png?expires=1784786400&signature=053938d9da6a3d0675a478b3d99312c86f3d262bffb9c57b9fa0ed189b3e59c4&req=dSUvFc95nYFcX%2FMW3nq%2BgQixcbGTz%2BPhBqlRXKOO%2BqeLV9fN%2FMWIPR8CVbA6%0ANzmUPppvZI2jBPbdR91WZmwRZ5c%3D%0A)

> Chats can be connected to an upstream page on the storyboard. Use the “Move to next page” behavior to allow navigating to a page, submitting, and then navigating to a chat.

If you want a user to start a conversation with the agent, click "Copy private link" to copy the page URL. You can then share this with your users.

Chat links can also be sent automatically in a workflow to kick off the conversation proactively, for example by using the "Send email" action, or a messaging template like Slack.

**Events**

Under the events tab you'll be able to delete or re-emit events, as well as view the details of each event. At the bottom of the events tab, you can choose to view the data as a conversation or JSON event.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1583652012/b27214881a7cddc3970445478f16/Convo+Event.gif?expires=1784786400&signature=7ef31b210dfbfdc2158ac080830b7098ea87cf02393aa46011e9a2555df73eb3&req=dSUvFc97n4FeW%2FMW3nq%2BgTXsHU1q8APzDdn8aNlO%2BwMWSS%2FX%2BY7TSwlFgt9%2F%0AFNdMDEC9Mo3toWR2kJuouMORHCw%3D%0A)

* * *

## Resources

**AI Agent action FAQs**

All your burning questions about agents answered [here](https://explained.tines.com/en/articles/11510164-ai-agents-faqs).

**How should I configure and use agents?**

Take a look at our best practices for using agents in Tines [here](https://explained.tines.com/en/articles/11644147-best-practices-for-the-ai-agent-action).

**Where can I find additional resources on agents?**

We've gathered some helpful Explained articles together [here](https://explained.tines.com/en/collections/9473753-ai-in-tines).

## Get started with page templates

Our new options for prebuilt page templates mean building pages in Tines just got easier. Choose to start with a blank page or pick from one of the preconfigured templates that align with your use case.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1553430680/5f4616896b9429eda95980cc2117/CleanShot+2025-06-03+at+11_58_18.gif?expires=1784851200&signature=4e4f4f0200fbf0e2ee4bb1e061d50e51e99b532ebfa7c08faa62e80edaaea65e&req=dSUiFc19nYdXWfMW3nq%2BgYrxVuq4TDRSiYOF1EdtHL0WpJpBLSf74WNAxBlf%0AiKPQZEs1srys5gIN2v0gf9C4qko%3D%0A)

These templates are intended as a starting point. We recommend familiarizing yourself with the [various page elements](https://www.tines.com/docs/pages/list-of-page-elements/) to then enhance the pages to fit your specific needs. The table below depicts the different available templates to choose from:

| **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 | A form to report issues and bugs. |
| Expense reimbursement | A form for an example of expense reimbursements. |
| Hardware request | A form for requesting hardware. |
| New employee form | An 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. |

> **TIP:** 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.

## Getting answers to your Tines questions

You can get answers to your Tines questions via our Community Slack, in-product chat, Tines Explained, Tines University, and email.

> **When you contact support, please follow [these guidelines](https://explained.tines.com/en/articles/8399626-hints-and-tips-from-the-tines-support-team) for an effortless support experience.**

### Community Slack

We have hundreds of experienced Tines builders in our community. We encourage all users to discuss their use cases and feature requests, share any jobs, and participate in our growing and vibrant Community! Click here to **[register](https://hq.tines.io/forms/6f8b122ccba3cb7e8e0d3531d1b70eb2)**.

Once registered, you can contribute and find answers from other Tines users and our team members.

### In-product assistance and tines Explained

You can access in-product help by clicking "Help and Support" and selecting "Chat with Support" on the bottom left of your screen. Click on "Help" to access **[Tines Explained.](https://explained.tines.com/en/)**

You can also contact the Tines support team, who will reply via chat.

### Tines University

The **[University](https://www.tines.com/hub)** is the primary resource for assistance with building in Tines, including information on self-hosting, action types, API, and formulas. Within the University, you can search across blogs, docs (including API), the library, and video lessons, and if necessary, **[create a case for the Tines Support team](https://www.tines.com/contact-support)**.

### Status page

If an issue with Tines affects multiple cloud-hosted customers, details will be provided at **[https://status.tines.com](https://status.tines.com/)** regarding the status, resolution, and cause of the issue.

### Email support

Once a case is created via the University, correspondence with the Tines Support team will be via email: **[support@tines.io](mailto:support@tines.io)**

-   You will receive an email acknowledgment with a case number.
    
-   To update the case and the Tines Support team, please reply via email.
    

### Tenant unavailable

If you can't access your tenant, check [https://status.tines.com](https://status.tines.com) for updates.

Click [here](https://www.tines.com/contact/?with-query=tenant-down) to request emergency support if you still need urgent assistance.

**Important Note:** Abuse of this facility may result in suspending support services.

This is available for paying customers of Tines only.

### Why Support?

When working with support, you work with a team that mitigates a single point of failure. We operate across multiple channels and are available 8x5 or 24x5, depending on your support contract. We also provide a query number. Once there is a number, there is an owner for your query.

Finally, we use your queries and our responses to enhance our training and technical content, which benefits all the users of Tines.

## Getting started with Tines

Accelerate your intelligent workflow strategy with the Tines edition fit to your needs. Sign up to community edition to get started with Tines today.

[Watch video](https://fast.wistia.net/embed/iframe/idz7p9tew3)

> If you want to do more with Tines, **[talk to our team](https://www.tines.com/book-a-demo/)** to find a solution that works for you. You can learn more about our pricing and packaging **[here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).**

#### Community edition

Our *forever free* Tines community edition is great for experimenting. You get:

-   Unlimited viewers
    
-   Unlimited parallel workflow runs
    
-   Unlimited integrations
    
-   Workflow essentials
    
-   Workflow controls
    
-   SSO
    
-   1 builder
    
-   3 flows
    
-   [All Tines AI features](https://explained.tines.com/en/articles/9398967-what-tines-ai-features-are-available-to-community-users)\*
    

\*Community edition tenants cannot bring their own AI models to Tines.

#### Limits

Below are the limits on the number of **events** and **flows** you can have in your Tines tenant.

**Events:**

| **Edition** | **Event limits** |
| --- | --- |
| Community | 25,000 monthly |

Learn more about events [here](https://explained.tines.com/en/articles/12688669-what-s-an-event-in-tines).

**Flows:**

| **Edition** | **Flow limits** |
| --- | --- |
| Community | 3 |

Learn more about flows [here](https://explained.tines.com/en/articles/10860654-what-is-a-flow-in-tines).

## Getting support while using Tines

As you use Tines, questions about how to get the most out of your workflows or how certain features work will naturally come up.

[Watch video](https://fast.wistia.net/embed/iframe/4axkoa6u1k)

Here are some of the ways you can get answers to these questions depending on what you're focused on.

-   [Community Slack](#h_cacdbe9bc2)
    
-   [In-product help](#h_70be10109c)
    
-   [Email support](#h_dfe81e1e7a)
    
-   [Customer Center](#h_4396f29532)
    
-   [Status page](#h_c6e77e21ee)
    

### Community Slack

We have thousands of experienced Tines builders in our community. We encourage all users to discuss their use cases and feature requests, share any jobs, and participate in our growing and vibrant Community! Click here to **[register](https://hq.tines.io/forms/6f8b122ccba3cb7e8e0d3531d1b70eb2)**.

Once registered, you can contribute and find answers from other Tines users, Community Champions, and team members.

### In-product help

You can access in-product help by clicking the "Support" option on the top right of your screen in the storyboard. Depending on your current screen, it may be on a submenu or a top-level item.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1803205751/3092d259e0888fd251a78fdbeaa9/CleanShot+2025-10-29+at+16_49_55%402x.png?expires=1784851200&signature=2b9a832bd654ddea2c80f4542830ee89cbabd2b68812798f82785101db4eb843&req=dSgnFct%2BmIZaWPMW3nq%2BgZKXAWmaaIQvavHuyRyebbC4U6I%2BzM9CnJ6y21DZ%0AdytPitDtcdMt%2FZF2wPL37I8DgvQ%3D%0A)

This will open a window where you can view your previous conversations, search our knowledge base, and access AI and human support.

##### **Your Support Experience with Tines + Intercom Fin AI**

Tines uses AI for all our conversational support channels to get the answers you need when needed. Our support system combines Tines' powerful automation with Intercom's Fin AI Agent to deliver:

-   **Instant Responses:** Receive immediate answers to common questions 24/7
    
-   **Intelligent Issue Resolution:** AI-powered assistance that understands your specific needs
    
-   **Seamless Escalation:** Complex queries are automatically routed to the right human expert
    
-   **Continuous Improvement:** Our Knowledge and Education teams learn from every interaction to serve you better through improving our documentation
    

If you need to speak to a technical support engineer while working with Fin, respond with "Talk to a Person", and the chat will be routed to a Tines support engineer.

> **💡 Note:** If you require support that does not leverage AI, you can email [support@tines.io](mailto:support@tines.io)

### Email Support

You can create a case via [support@tines.io](mailto:support@tines.io)

-   You will receive an email acknowledgment with a case number.
    
-   To update the case and the Tines Support team, please reply via email.
    

### Customer Center

The **[customer center](https://www.tines.com/customer-center/)** is the primary resource for all things Tines. It includes links to webinars, events, Tines University, a knowledge base, and documentation. You can also contact support via the support pop-up at the bottom right of the screen.

### Status page

If there is an issue with Tines that affects multiple cloud-hosted customers, details will be provided at [https://status.tines.com](https://status.tines.com) regarding the status, resolution, and cause of the issue.

We recommend you subscribe to the status page to ensure awareness of issues that may affect your tenant.

## GitGuardian Authentication Guide

GitGuardian is a global cybersecurity startup focusing on code security solutions for the DevOps generation. GitGuardian helps developers, cloud operation, security, and compliance professionals secure software development define, and enforce policies consistently and globally across all their systems.

## First, get a GitGuardian API token

1.  Login to your GitGuardian account
    
2.  Click "API"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889120846/50a88dd96fb33b6cc5566937/image.png?expires=1784851200&signature=eb4a407c1e79568010d4c07ea5363a1845900b99d378605e9cd9508e6b61cd38&req=fCguF8t%2BlYVZFb4V1XW4gR8h1rfoVRAlJylB4%2BiWM49eiarMxV8%2Bt5kO%2FpGe%0AerRsjXq9EoygbdYUuod4qarZkQ%3D%3D%0A)
    
      
    ​
    
3.  Click "Personal access tokens" and then click "+ Create token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889121710/1dc25fa2a282be64c7e78beb/image.png?expires=1784851200&signature=f49f26d4207f0f7a3ca66297a2bf258d7fae820568a45151d2ffb6bf80acb815&req=fCguF8t%2FmoBfFb4V1XW4gXhH3zsgBsxxhMwj6f58TTUoIbM4FHW%2FmywHPdL7%0A2aHDsQ7%2FU2%2FXoQmyck4fQMz5Zw%3D%3D%0A)
    
      
    ​
    
4.  Name the token, choose an expiry period, select the required scopes, and then click "Create token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889122615/a5a0b19117f4d49a9cfea3b9/image.png?expires=1784851200&signature=a1d4eddef5023504f35f629992952a453b7e0406a51ce7d81f9a81802beeaf25&req=fCguF8t8m4BaFb4V1XW4gX9SAp6OZrtwNwadgVJPmV4ArseDvUJAeyH4UBch%0A85%2FTWGALmHSs48BQ0ZhdgJ13BQ%3D%3D%0A)
    
      
    ​
    
5.  Copy your token to a text editor
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/889123646/4dc039eae42c6b2bd777ff1d/image.png?expires=1784851200&signature=1913088391eb5f5f6f8a350048979e1e98e60125809c001a8a73a0ac48237061&req=fCguF8t9m4VZFb4V1XW4gf%2FklM9dx9AcLo%2FiAjQQn%2BZrvtbF9wKIhEgt2%2Fb%2B%0AUmGJSjoeBTA6zw1Xpcr4ZUiHjQ%3D%3D%0A)
    

## Lastly, create a GitGuardian credential in Tines

**GitGuardian connect flow:**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "GitGuardian" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/gitguardian).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of GitGuardian** **stories in the [story library](https://www.tines.com/library?s=gitguardian&view=all)**

## GitHub Apps Authentication Guide

**GitHub, Inc**. is an Internet hosting service for software development and version control using Git. It provides the distributed version control of Git plus access control, bug tracking, software feature requests, task management, continuous integration, and wikis for every project.

To authenticate with a [GitHub Apps](https://docs.github.com/en/apps/overview) installation token, you will need the following:

-   "Client ID" of the GitHub App - Required to generate a JWT token to obtain an installation token.
    
-   "Private key" of the GitHub App - Required to generate a JWT token to obtain an installation token.
    

## Firstly, create a GitHub App in GitHub

1.  Navigate to the GitHub Apps page: [https://github.com/settings/apps](https://github.com/settings/apps)
    
2.  Click "New GitHub App"
    
3.  Fill out "GitHub App name", "Homepage URL" and make a selection for where the GitHub App can be installed.
    
4.  Click "Create GitHub App"
    
5.  Click "General"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1208272426/0b0fc40930db57cd47e4f54d0ed5/image.png?expires=1784851200&signature=5f18b54d92dfe7e646eb3df72c2f597afb313bf8c436900bb48b0ee5f954694b&req=dSInHst5n4VdX%2FMW3nq%2BgXvtdAmSw6NMvhgrJmFGP7RJTVXumSPwD2QRgZO%2B%0AbRpJTd7YplfN3XpxyWayJTuH%2BWU%3D%0A)
    
6.  Copy your "Client ID" to a text editor  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1208279490/88a0f0c39cee33243019c1b302b5/image.png?expires=1784851200&signature=5333efba45a130b37abeb84f6e37df44ac86c6277207d5b0971b4789272cf217&req=dSInHst5lIVWWfMW3nq%2BgYbUYUotN76fV1Nj1oI8p9l6WRObD37L1LsepPHd%0AXYMq4Jzxl7b5whn2uHDz8iFwMAQ%3D%0A)
    
7.  Click "Generate a private key" to download a private key  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1208272901/ddddeb52623d04dc1fe74c688436/image.png?expires=1784851200&signature=22b440c6442187965044ade0fff8b12049636b2c915271dfb8f7fe4c8771a4a9&req=dSInHst5n4hfWPMW3nq%2Bgd7%2FZonmncZMe9czsVamvWVgkAyBvdaJ99e3uwhH%0A%2BjKAsfh54QrhkQM5BN%2FWzJFb6eY%3D%0A)
    
      
    ​
    
8.  Click "Permissions & events" and select the relevant permissions
    
9.  Click "Install App" and install it to the required account/organisation
    

## Next, create resources and credentials in Tines

You will need to create the following:

-   Resources: "GitHub Org" / "GitHub Username", "GitHub Client ID"
    
-   Credentials:
    
    -   Text type: "GitHub Private Key"
        
    -   Multi Request type: "GitHub Installation Token"
        

##### Resources

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resources"
    
3.  Click "+ New"
    
4.  Input the values for the GitHub resource
    
    -   **Name:** GitHub Org / GitHub Username
        
    -   **Description:** Optional
        
    -   **Builder:** Your GitHub org/username
        
5.  Click "Save resource"
    
6.  Click "+ New"
    
7.  Input the values for the GitHub resource
    
    -   **Name:** GitHub Client ID
        
    -   **Description:** Optional
        
    -   **Builder:** Your GitHub App's client ID
        
8.  Click "Save resource"
    

##### Credentials

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New" and select "Text"
    
3.  Input the values for the GitHub credential
    
    -   **Name:** GitHub Private Key
        
    -   **Description:** Optional
        
    -   **Value:** Your GitHub private key
        
        -   Ensure that "Preserve newline characters" is selected
            
4.  Click "Save"
    
5.  Click "OK" to domain restriction warning.
    
    -   Ensure that no domain is entered as the credential won't be able to be used in the Multi Request type credential.
        
6.  Click "+ New" and select "Multi Request"
    
7.  Input the values for the GitHub credential
    
    -   **Name:** GitHub Installation Token
        
    -   **Description:** Optional
        
8.  Under **HTTP Request 1**, click "<> Edit as JSON" and paste the following JSON:  
    ​
    
    ```
    {
      "url": "https://api.github.com/orgs/<<RESOURCE.github_org>>/installation",
      "content_type": "application_json",
      "method": "get",
      "headers": {
        "Authorization": "Bearer <<JWT_SIGN(LOCAL,CREDENTIAL.github_private_key)>>"
      },
      "local": {
        "iat": "=NUMBER(DATE(\"60 seconds ago\",\"%s\"))",
        "exp": "=NUMBER(DATE(\"10 mins from now\", \"%s\"))",
        "iss": "=RESOURCE.github_client_id"
      },
      "customize_output": "=SET_KEY(\n  OUTPUT,\n  \"jwt\",\n  JWT_SIGN(\n    OBJECT(\n      \"iat\",\n      NUMBER(DATE(\n        \"60 seconds ago\",\n        \"%s\"\n      )),\n      \"exp\",\n      NUMBER(DATE(\n        \"10 mins from now\",\n        \"%s\"\n      )),\n      \"iss\",\n      RESOURCE.github_client_id\n    ),\n    CREDENTIAL.github_private_key\n  )\n)"
    }
    ```
    
    -   For an app installed in a user account, change the `url` to `https://api.github.com/users/<<RESOURCE.github_username>>/installation`
        
9.  Click "Done"
    
10.  Click "Save"
     
11.  Under **HTTP Request 1**, click "Run request" and confirm that the request is successful
     
12.  Under **HTTP Request 2**, click "<> Edit as JSON" and paste the following JSON:  
     ​
     
     ```
     {
       "url": "<<PREVIOUS_STEP.github_installation_token.body.access_tokens_url>>",
       "content_type": "application_json",
       "method": "post",
       "payload": {},
       "headers": {
         "Authorization": "Bearer <<PREVIOUS_STEP.github_installation_token.jwt>>"
       }
     }
     ```
     
13.  Click "Done"
     
14.  Click "Save"
     
15.  Under **HTTP Request 2**, click "Run request" and confirm that the request is successful and double click on the key named `token` in the response.
     
16.  **Location of token from response:** Paste the value copied in the previous step, i.e. `github_installation_token.body.token`
     
17.  Click "Save"
     

## Lastly, using the credential in an action

When you make an API request, include the Multi Request type credential in the `Authorization` header after "Bearer" like:

```
Bearer <<CREDENTIAL.github_installation_token>>
```

An example action to get the contents of a repo you can copy and paste onto your storyboard:

```
{"standardLibVersion":"62","actionRuntimeVersion":"14","agents":[{"disabled":false,"name":"Get repo contents","description":null,"options":"{\"url\":\"https://api.github.com/repos/<<owner>>/<<repo>>/contents\",\"content_type\":\"application_json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.github_installation_token>>\"}}","position":{"x":5715,"y":300},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"caseConfiguration":{"subStatus":null},"recordType":null,"recordWriters":[],"form":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:86b7576feeb7d3b547f14441bd59f25c"}],"links":[],"diagramNotes":[]}
```

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of GitHub stories in the [story library](https://www.tines.com/library/?s=github&view=all)**

## GitHub Authentication Guide

**GitHub, Inc**. is an Internet hosting service for software development and version control using Git. It provides the distributed version control of Git plus access control, bug tracking, software feature requests, task management, continuous integration, and wikis for every project.

## Firstly, create a credential in Tines,

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "OAuth 2.0"
    
4.  Copy the "Callback URL"  
    ​
    

## Next, create a GitHub OAuth App

1.  Navigate to [http://www.github.com/](http://www.github.com/) and sign in. If you don't have an existing account, click on "Sign up"
    
2.  Click on your profile in the top right-hand corner and select "Settings"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772238877/e78eb6fb0da6168fcdd69540/image.png?expires=1784851200&signature=0ffa6b270a12f86df843a1e6dc91b21d54d55ca605340b860bf7073d2ea5ee8e&req=cyclFMp2lYZYFb4V1XW4gaNC2%2BrqruzB%2FCEOU%2BXqhAUV3izRBtImg2nSkC%2By%0AvqcMsmJ13ArNwzdM9nnkCi7n2A%3D%3D%0A)
    
3.  Click on "Developer Settings" at the bottom of the sidebar on the left-hand side.  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772239248/ed396bd0c417ce5432055ec5/image.png?expires=1784851200&signature=b0d6cb8b3e6da829e40b52f4f1579f0cf2f439effea22d03d9f269dcd160920b&req=cyclFMp3n4VXFb4V1XW4gQTMwmR2LmJAmFwpmFvG4OPmK6yupeu%2BwaXVtn8J%0AhYLuWpZFwKTCUOLek9FbsKmGvQ%3D%3D%0A)
    
4.  Click on "OAuth Apps" and then "Register a new application"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772239651/1243306a7b1d4f2a303d1011/image.png?expires=1784851200&signature=cafb85d7eae94e4ae91893e843ffc53a54b3d2dc52d43d14d1a444b64b093d30&req=cyclFMp3m4ReFb4V1XW4gcugQ%2Bh2rWJIgH0cvTs28bupu5oEtGPFoRDWTqBc%0ACskUV540soHc3ngn%2F41UJMHuag%3D%3D%0A)
    
      
    ​
    
5.  Fill out the application details, using the "Callback URL" copied from your Tines credential for "Authorization callback URL". Once completed, click "Register application"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772247292/e40a2292732f42a422460af6/image.png?expires=1784851200&signature=bcfbc123d32a0ed2878a53548dc79e0f84592059b4961c4a0fad7eb9666bff13&req=cyclFM15n4hdFb4V1XW4gZnUWikaEBGsdQ%2BhkIwXqp6HgQP%2FHf%2Fwdnwhajis%0A3I8oQo4TafJY3%2B3WACw9fhizHw%3D%3D%0A)
    
6.  Click "Generate a new client secret" and copy the secret generated, along with the "Client ID"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772242008/b1260851e7f2145e3fa8716e/image.png?expires=1784851200&signature=2f0b04f01fafc4546cc8bd482da50dc3bf08dfd549ebc46ddec58cad5a85ea1d&req=cyclFM18nYFXFb4V1XW4gZscx%2FZGaWcsXyT191TaOxY27WVgQqEn%2BF%2Fe73%2Bj%0A7c3D00%2F554urQ6Ya7hKV%2Bq7M4A%3D%3D%0A)
    
      
    ​
    
7.  Click "Update the application"
    

## Lastly, continue creating the credential in Tines

1.  Input the values for the GitHub credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Client ID:** "Client ID" copied earlier
        
    4.  **Client secret:** "Client secret" copied earlier
        
    5.  **Scope:** repo user
        
    6.  **OAuth Provider:** Manual
        
    7.  **Grant type:** Authorization code
        
    8.  **OAuth authorization request URL:** `https://github.com/login/oauth/authorize`
        
    9.  **PKCE challenge method:** None
        
    10.  **OAuth token URL:** `https://github.com/login/oauth/access_token`
         
2.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
3.  When you click "Save", you may be asked to "Authorize Tines"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/772246422/1c31aacecab1aece42d1ee5a/image.png?expires=1784851200&signature=fcaca61b6a6292b7b4633943ce01a5c8eb94dad18e4e2f0f6e85627c7540d2a2&req=cyclFM14mYNdFb4V1XW4gapgdpD4mY3bNaJ6xibOcjTX%2FnLpz8Arxm3Z723j%0Av%2BUZG8pEpyyJtljL3IFFpGinOA%3D%3D%0A)
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of GitHub stories in the [story library](https://www.tines.com/story-library?s=github)**

## Google Workspace Service Account Authentication Guide

Google Workspace is a suite of cloud-based productivity and collaboration tools offered by Google, formerly known as G Suite. It includes applications such as Gmail, Google Drive, Google Docs, Google Sheets, Google Slides, Google Calendar, Google Meet, and more.

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 users. For more information, see [understanding service accounts](https://cloud.google.com/iam/docs/understanding-service-accounts).

This authentication guide will guide you through configuring a service account and corresponding Tines credential for Google Workspace.

### Create a Google credential in Tines

There are 3 ways to create your Google credential in Tines:

1.  ##### **Google Service Account connect flow (*recommended*)**
    
    This connect flow will guide you through the process of creating your service account and creating a Google credential for you. See [How to configure the service account in Google](#h_bf1d128925) for specifics on scopes and roles for your service account with Google workspace.
    
    1.  Navigate to the team that will be using the API and click **Credentials**
        
    2.  Click **\+ New Credential** and select **Google Service Account** and follow the prompts to connect.
        
2.  ##### **Actions on the storyboard**
    
    If you will be impersonating other users at a rapid rate as part of your Tines workflow, it may be preferred to generate this credential as part of the workflow via actions on the storyboard. While you *can* update the user input in a credential, it may lead to race conditions if you have multiple requests to update the credential at a time. Hence, many users opt to create the token needed in each story run, avoiding race conditions.
    
    1.  Follow the steps to [configure a service account in Google](#h_bf1d128925)
        
    2.  Save the service account email as a resource in Tines:
        
        1.  Navigate to the team that will be using the API and click **Resources**
            
        2.  Click **\+ New resource**, name it `google_svc_account_email`, and paste the service account email into the builder of the resource, select **Save resource**.
            
            ![](https://downloads.intercomcdn.com/i/o/1008493650/4dbc98e4d6428d161ccf2542/resource.png?expires=1784851200&signature=c93f74dc2852567517b6bed787cebe7bf2edbd22d20cbd892c155a9ad84d3adc&req=dSAnHs13nodaWfMW3nq%2BgYvKOgJprnOfhnDXMRLxkSPDOfA42%2FVJoYoGoynm%0A9YY2Gedy42I78YOJ4NZhCzRvJ5Q%3D%0A)
            
    3.  Save the private key generated as a text credential in Tines:
        
        1.  Navigate to the team that will be using the API and click **Credentials**
            
        2.  Click **\+ New credential**, and select **Text**
            
        3.  Name the credential `google_svc_key`, select **Preserve newline characters** and paste the private key from your generated JSON file into the value. This starts with `-----BEGIN PRIVATE KEY-----` and ends with `-----END PRIVATE KEY-----\n`, select **Save**.
            
    
    1.  Import [this story](https://www.tines.com/library/stories/1235478/generate-a-google-service-account-credential) to your tenant. This is a Send to Story that generates a token and sends that generated token back to the story that called it. There is a sample action in the storyboard showing how to call the story and then use the generated token in any downstream actions. You can use these sample actions to then generate this token in any of your workflows.
        
3.  ##### **Manual credential creation**
    
    This method is not recommended as the [Google Service Account connect flow](#h_645b108864) creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:
    
    **Step 1: Create the JWT credential**
    
    1.  Follow the steps to [configure a service account in Google](#h_bf1d128925)
        
    2.  Click **\+ New credential**, and select **JWT**
        
    3.  Name the credential `google_svc_jwt_token` or a unique name of your preference
        
    4.  Select Algorithm RS256.
        
    5.  Understand the payload component of the JWT credential. The fields are described below:
        
        ![](https://downloads.intercomcdn.com/i/o/1008540376/b47b35a6897d2ba80ba79273/jwt.png?expires=1784851200&signature=cc174b9c8dc46d9427ff6d74821f1a1c7246dcac4de82ee486cf843fa595bcc8&req=dSAnHsx6nYJYX%2FMW3nq%2BgQc1Mx3TGrMFA3MVdenO5I969E4fA9FwFEr8dqVi%0A7dgtKNojvLU7Dy3bOEVqwwVEh0o%3D%0A)
        
        See a sample JWT payload for a Google service account:
        
        ```
        {
          "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
          "scope": "https://www.googleapis.com/auth/devstorage.read_only",
          "aud": "https://oauth2.googleapis.com/token",
          "exp": 1328554385,
          "iat": 1328550785
        }
        ```
        
    6.  Complete the JWT payload as follows:
        
        1.  `iss`: The email address of the service account. This is the `client_email` value in your private JSON key.
            
        2.  `sub`: The email address of an admin in your domain that has access to manage users and groups. The service account will impersonate this user when performing actions defined in your workflows.
            
        3.  `scope`: Space-separated list of the scopes authorized for your service account.
            
            ```
            https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/admin.directory.user
            ```
            
        4.  `aud`: Insert `https://oauth2.googleapis.com/token`
            
    7.  Select the **Auto generate ‘iat’ (Issued At) & ‘exp’ (Expiration Time) claims** checkbox. Tines will automatically add “iat” and “exp” claims to the payload according to when the credential is used.
        
    8.  Copy and paste the private key from your private key file into the Tines credential. This starts with `-----BEGIN PRIVATE KEY-----` and ends with `-----END PRIVATE KEY-----\n`
        
    9.  When complete, the credential page should look similar to the below:
        
        ![](https://downloads.intercomcdn.com/i/o/1008548637/0d4b73feb4e1e58b2179ff11/manual-jwt.png?expires=1784851200&signature=59415deffb4381ec5143d076f0c21480549fe19418f97967d38c10102a1a6cf2&req=dSAnHsx6lYdcXvMW3nq%2BgbRJyP%2Fk145lrZjTRnB3TPnWz%2FPPPRIUGAlCBz3x%0AyDpnkf0L4zG3YNvo%2F2s9V2xaafE%3D%0A)
        
    
    **Step 2: Create the HTTP Request credential**
    
    Once the JWT credential is created, you will reference this credential within another HTTP request credential to get the access token needed for your API requests to Google to work.
    
    1.  Confirm you've completed **step 1: Create the JWT credential**
        
    2.  Click **\+ New credential**, and select **HTTP Request**
        
    3.  Name the credential `google` or a unique name of your preference
        
    4.  Scroll down and select the button `Edit as JSON` and copy the below payload in:
        
        ```
        {
          "url": "https://oauth2.googleapis.com/token",
          "content_type": "form",
          "method": "post",
          "payload": {
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": "=CREDENTIAL.google_svc_jwt_token"
          }
        }
        ```
        
    5.  Select **Done**
        
    6.  In the `Domains` field, enter `*googleapis.com` to restrict this credential to only being used in requests to that domain.
        
    7.  You're not done yet! Select **Save** for the Credential to save and re-open.
        
    8.  Scroll down and select **Run request**. This will run the HTTP request and show the results in JSON. If you successfully configured your service account, JWT credential, and HTTP request credential correctly, you will see a 200 status returned.
        
    9.  Copy the path to the access token in the successfully run request and paste the value in the section `Location of token from response`. This will be referenced each time an action is run in your workflows that calls this credential.
        
        [Watch video](https://www.youtube.com/embed/r-pr4a98m5Y?rel=0)
        
    10.  Select **Save**
         
    11.  You will now reference this HTTP request credential within your requests to Google on the storyboard. Try the Google Admin Console action templates to test this credential in a story. You will note that the templates reference a key `CREDENTIAL.google` in the authorization headers. If you named your HTTP request credential something different, update this reference for it to run successfully.
         

### How to [configure the service account in Google](https://developers.google.com/workspace/guides/create-credentials#service-account)

1.  Enable the relevant API library for your service account
    
    -   Go to [API Library](https://console.cloud.google.com/apis/library) and select your relevant Google Cloud project in the top left corner.
        
    -   Search for and enable the [Admin SDK API](https://console.cloud.google.com/apis/library/admin.googleapis.com).
        
2.  Create a service account and a key
    
    -   Go to [Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts) in the Google Cloud Console and select a project if prompted
        
    -   Select **Create service account**
        
    -   Enter a name for the new account and select **Create and continue**
        
    -   Select the `Service Directory Admin` role and select **Continue** and then **Done**. This role is used for demo purposes only and not required for production. You should use a role appropriate to your needs.
        
        ![](https://downloads.intercomcdn.com/i/o/754288978/2866fa34917ebfaabe99c42c/Screenshot+2023-06-01+at+13.30.51.png?expires=1784851200&signature=c6f014c6e6ebc1817e889fb5d2a82c2ab810365dc9057a74db134442f42c02c4&req=cyUjFMF2lIZXFb4V1XW4gc83phHSgwxuRtBmU%2FXYydqfYTHDZx%2FW%2Fx9i7u9x%0Any5hCi3fWY3j81TRi7q4MXjq%2Bg%3D%3D%0A)
        
    -   Click to open the newly created service account and select **Keys**
        
    -   Select **Add key** and **Create new key**
        
    -   Select **JSON** and select **Create**
        
    -   Your private key (JSON file) will begin downloading to your computer. **Keep this file safe; it contains secret information and cannot be downloaded again**
        
3.  Determine the [scopes](https://developers.google.com/identity/protocols/oauth2/scopes) to assign to your service account. Scopes provide a way to limit the amount of access that is granted to an access token or application. For Google Workspace, you can consider the below scopes:
    
    -   [https://www.googleapis.com/auth/admin.directory.group](https://www.googleapis.com/auth/admin.directory.group)
        
    -   [https://www.googleapis.com/auth/admin.directory.user](https://www.googleapis.com/auth/admin.directory.user)
        
        -   A full list of OAuth 2.0 Scopes for Google APIs is available here.
            
4.  Authorize the service account
    
    For Tines to access user data in Google Workspace, a Google Workspace administrator needs to authorize the service account created in the prior steps, this is a process known as delegating domain-wide authority.
    
    -   Go to [Domain wide delegation settings](https://admin.google.com/ac/owl/domainwidedelegation)
        
    -   Select **Add new**
        
    -   In the **Client ID** field, enter the client ID of your service account. This can be found under the `client_id` field in the private key file you downloaded and is typically a long number.
        
        ![](https://downloads.intercomcdn.com/i/o/754295710/6024d853a50ff1d10bdfaa73/Screenshot+2023-06-01+at+13.39.35.png?expires=1784851200&signature=19c3149993bc8808a0d48ec5eba3ca47cfac19fa7df62c15a8dfcd881a65c677&req=cyUjFMB7moBfFb4V1XW4geyj185o6cXddZPHGZfWQF08uI315D9rprQa3jlv%0ARw11KDGJ5dZ3AwWxtV8uhAsyAA%3D%3D%0A)
        
    -   In the OAuth scopes (comma-delimited) field enter:
        
        ```
        https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/admin.directory.user
        ```
        
    -   Select **Authorize**
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/google-service-account).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).  
> You can find a selection of Google stories in the Tines [story library.](https://www.tines.com/story-library?s=google&page=1)

## GreyNoise Authentication Guide

**[GreyNoise](https://www.greynoise.io/)** captures data on IPs that scan the internet and saturate security tools with internet noise, so SOC teams can focus on threats that matter and ignore those that don’t.

### First, get a GreyNoise API key

1.  Go to [https://www.greynoise.io/](https://www.greynoise.io/) and login with an existing account or sign-up for a new account
    
2.  Click on your profile and select "My API Key"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/834575186/33b0fe1ca7edf0739bee6711/image.png?expires=1784851200&signature=756c21877205594bea7c48dbcc935ed3ccf06f01c8bada6b558037f561f4f702&req=fCMjE857nIlZFb4V1XW4gUp5AfDbP3AEBXdplLIlNoCnwwRUtdPk%2BAOKNRGG%0AvJYaJgNbuNHCby%2FPzRzWpcglXg%3D%3D%0A)
    
3.  Click "View API Key" to show the API key and copy it  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/834578496/2d7446b3deda225dd6b884fa/image.png?expires=1784851200&signature=7db1aa5eb565af6b3cebb21b127e95c237fc289e141c64e3e298bcf05497c089&req=fCMjE852mYhZFb4V1XW4gU27CvgNL4SEqOKvnGxACax9uCvEOI21wbCfuNrc%0A9ZdZxZHP0G1vL5VdRR65mnvaiQ%3D%3D%0A)
    
      
    ​
    

### Then, create a GreyNoise credential in Tines

**GreyNoise connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "GreyNoise" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the GreyNoise connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the GreyNoise credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/greynoise).**
>
> **For more on creating credentials in Tines click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of GreyNoise stories in the [story library](https://www.tines.com/library?s=greynoise&view=all)**

  
​

## Halo PSA/ITSM Authentication Guide

HaloITSM is a flexible, all-inclusive ITSM (IT Service Management) solution. HaloITSM gives you the power to manage all IT support with powerful ITIL-aligned out-of-the-box functionality. You can streamline and report on specific processes such as change and problem management. Tailored to your exact requirements, you can align IT to your business needs in an all-inclusive, unlimited IT Service Desk solution.

### First, create a Halo PSA/ITSM application

1.  Login to your Halo PSA tenant
    
2.  Click on "Configuration"
    
3.  Under "Advanced", click "Integrations"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/832741761/5204b12e48aa6d1b904b59ab/image.png?expires=1784851200&signature=abd1c443c753969bbebdf540f96f0de022a4e879c0dae20cea17a6c46abd9c2b&req=fCMlEc1%2FmodeFb4V1XW4gV1VAMrIAEOhdDqTafmIYCAtia6Or9oNIQNvufEC%0AY3eDKXlxZJwVoJxN321My1iOCA%3D%3D%0A)
    
      
    ​
    
4.  Next, click "HaloPSA API"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/832742514/aaa667655fdf074f02b3bf11/image.png?expires=1784851200&signature=94d96545b24f2a01ecea05d4e59430691324a0ddc2ce2b4d05098920648e78e6&req=fCMlEc18mIBbFb4V1XW4gTBLtTF5AnFDBH%2FJ%2F8aEtifNiijFaSHozP7mhd0n%0AchXrV0TrNXFgBYr6tyW6PIrp1g%3D%3D%0A)
    
      
    ​
    
5.  Copy the values for "Resource Server", "Authorisation Server" and "Tenant", and then click "View Applications"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/833628702/8cbe59b8ac2cd5a3c94065fe/image.png?expires=1784851200&signature=980c159cd41a4d5c2a403cd0c2619bcbb854fffe59f1ece52684af25f9927ddc&req=fCMkEMt2moFdFb4V1XW4gVmyHM58Wityo2d4M35MhFq8f8jLA5hT2SZothBh%0ACXg%2FoWFuLN5lMFg2HJqEKOgsqg%3D%3D%0A)
    
      
    ​
    
6.  Click "New" to create a new application  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/833629368/5d59ed101c53c1e372801da6/image.png?expires=1784851200&signature=74e7ddcb0b0cd28e804d21e79110b24a7bee6fcb786c72ce0d4e2b83cdacfc13&req=fCMkEMt3nodXFb4V1XW4gT0QKqdp%2FQvi%2Bzvn8GTIhNzpnnRdze9mu%2F8oQDix%0A4ZDAhJysw1YatjZ68VMHr9Ix5A%3D%3D%0A)
    
      
    ​
    
7.  Give the application a name, select "Username & Password" for the "Authentication Method" and then copy the "Client ID"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/833630902/b7835f4bce8ad9652c02caa8/image.png?expires=1784851200&signature=a9cadc5f6b2758c53de10ad3e5b1957d712f4ff94bfd6de6959d496e93310a88&req=fCMkEMp%2BlIFdFb4V1XW4gcpaVC1YzQyrcfn%2BCY0%2BMid4IKNWC77CQPITnote%0AoHxTsxo%2BDrlXsFhNGaCShbiUWA%3D%3D%0A)
    
      
      
    ​
    
8.  Next, click on the "Permissions" tab and choose the required privileges  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/833636645/22a5407a13788017f9006de4/image.png?expires=1784851200&signature=1142b8a239b276afe551902c2c282425f6207082b5eb6a4014548b38e214b6b8&req=fCMkEMp4m4VaFb4V1XW4gYf0NQp0DTQsMh%2ByerxmpF7NfwOhdy%2BwLcoIQ6jK%0A%2B3Y3dY3dLeSr6ucvzonKR%2Br7uQ%3D%3D%0A)
    

### Next, obtain a Refresh Token in Tines

1.  Login to your Tines tenant
    
2.  Create a new story
    
3.  Copy the below action to your storyboard, replace the values and run the action
    
    1.  You will need to replace the following values:
        
        1.  `<<AuthorisationServerName>>` with your "Authorisation Server"
            
        2.  `<<YourTenantName>>` with the your "Tenant"
            
        3.  `<<YourClientID>>` with your "Client ID"
            
        4.  `<<UserName>>` with your "Username"
            
        5.  `<<Password>>` with your "Password"  
            
    
    ```
    {"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Request to HALOPSA to Get Auth Token","description":null,"options":"{\"url\":\"https://\\\\<<AuthorisationServerName>>/token?tenant=\\\\<<YourTenantName>>\",\"method\":\"post\",\"content_type\":\"form\",\"headers\":{\"accept\":\"application/json\"},\"payload\":{\"grant_type\":\"password\",\"client_id\":\"\\\\<<YourClientID>>\",\"username\":\"\\\\<<UserName>>\",\"password\":\"\\\\<<YourPassword>>\",\"scope\":\"all\"}}","position":{"x":90,"y":735},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:536b51f8b592b6b269124bde91592b8d:bb39f16bb96afe7fca6079734f25f6e6"}],"links":[],"diagramNotes":[]}
    ```
    
4.  Copy the `refresh_token` value
    
    ![](https://downloads.intercomcdn.com/i/o/833649508/e2dd6cefb8b4030508a4abf2/image.png?expires=1784851200&signature=d67e73030b8cf9e524de2336686a9000bf8082053c0e3a5dc580604918d63e17&req=fCMkEM13mIFXFb4V1XW4gYohP9CO0sP5bfmfaBn%2FbW5vnZJblp5RENSq5SV3%0AiHIQXS8Sp069TnAxoNkoHYcviQ%3D%3D%0A)
    

### Lastly, create a Halo PSA/ITSM credential in Tines

1.  Navigate to the team that will be using the API and click "Credential"
    
2.  Click "+ New Credential" and select "HTTP Request"
    
3.  Input the values for the Halo PSA/ITSM credential
    
    -   **Name:** halo
        
    -   **Description:** Optional
        
    -   **URL:** `https://<<AuthorisationServerName>>/token?tenant=<<YourTenantName>>`
        
    -   **Content Type:** Form
        
    -   **Method:** post
        
    -   **Payload:** Copy the below object and paste into the "Plain code" section, replacing:
        
        1.  `<<client_id>>` with your "Client ID"
            
        2.  `<<refresh_token>>` with your "Refresh Token"
            
        
          
        ​
        
        ```
        {
        "grant_type":"refresh_token",
        "client_id":"<<client_id>>",
        "refresh_token":"<<refresh_token>>"
        }
        ```
        
          
        ​
        
    -   **Headers:** Click "+" and add the header `"accept":"application/json"`
        
    -   Click "Run options" and make sure you get a successful response.
        
    -   **Location of token from response:** Double-click the `access_token` key in the response and paste into the pill. It should read `halo.body.access_token`
        
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/833697028/c5a069e726d3bc49f9a2106f/image.png?expires=1784851200&signature=848b1909749f0afd22d4c95cb426e500feaf4d58009949431eb9510631e80122&req=fCMkEMB5nYNXFb4V1XW4gbFaylftKA16p06AuMHTUA8J8f2YavdCVHd8W%2Fqs%0AhjUKYMBSZiNbvDJ0ejg078DHlA%3D%3D%0A)
        
        ![](https://downloads.intercomcdn.com/i/o/833697367/5f7ddfa3fe9abe848eabbeb0/image.png?expires=1784851200&signature=e1e4c133f48678697222af76e37bbf7dd4253b030447a852d2d903b834a3cecb&req=fCMkEMB5nodYFb4V1XW4gQlKnJhIezsWdTMiVWPEoKWkW8WBXHTiKoPWfnqO%0AalMQNudnVOuXPIG9VsofenOtYw%3D%3D%0A)
        
          
        ​
        
4.  **Optional**
    
    -   **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    -   **Access:** What other teams can also use the API
        
5.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/833702357/8a5b86c9347f40383b4b7824/image.png?expires=1784851200&signature=edd4810d8b4925d0a3d7ad93d33ce094b5814c8616086055b9255e9bd915202d&req=fCMkEcl8noRYFb4V1XW4gfJA5v417phtVfEUx0L0Z7NAe1Pn7EHa1bn%2FWoCY%0ARHC1kPYmZT%2BqoxQ0Tsr1abtcnw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/833702730/eb63ef1025d307229649570f/image.png?expires=1784851200&signature=003210701cf174b88ea2fe356d6c07a73bfd4109cfd7df38989931703726e86e&req=fCMkEcl8moJfFb4V1XW4gRXIVsDxOy3ofQ27yQ2JXrgS4sgPrqcDWzxmp6Ij%0ArejheIbZavRImUpOF0KeiFY2BQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/833703338/3f83a7de246d30107c7c9d1c/image.png?expires=1784851200&signature=4d6a60e3fec19e0ca812dd263a7607701a81ea14c572f1c50092b5b03fa736be&req=fCMkEcl9noJXFb4V1XW4gWDLF7Sf3VSfm%2Bv1gaTKBHldlXLBCedWcsGrIdjb%0AjBWYkHTt%2F71LmpUKIdJtBZgVIA%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Halo PSA/ITSM stories in the [story library](https://www.tines.com/library?s=halo&view=all).**

### Using the credential in an action

Below is an example Halo PSA/ITSM action you can copy and paste onto your storyboard in Tines. You will need to replace `YourResourceServer` with your "Resource Server"

```
{"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Get a list of Tickets","description":null,"options":"{\"url\":\"https//YourResourceServer/api/Tickets\",\"method\":\"get\",\"content_type\":\"json\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.halo>>\"}}","position":{"x":1980,"y":1305},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:536b51f8b592b6b269124bde91592b8d:bb39f16bb96afe7fca6079734f25f6e6"}],"links":[],"diagramNotes":[]}
```

## Have I Been Pwned Authentication Guide

HaveIBeenPwnd is free resource for anyone wanting to quickly assess if they may have been put at risk due to an online account of theirs being compromised or "pwned" in a data breach.

### First, get a HaveIBeenPwned API key

1.  Login to/Signup to HaveIBeenPwned and purchase a key at [https://haveibeenpwned.com/API/Key](https://haveibeenpwned.com/API/Key).
    
    -   Once you've purchased a key, it will be emailed to you
        

### Lastly, create a HaveIBeenPwnd credential in Tines

1.  **Have I Been Pwned connect flow (recommended):**
    
    1.  Login to your Tines tenant
        
    2.  Navigate to the team that will be using the API and click "Credentials"
        
    3.  Click "+ New Credential" and select "Have I Been Pwned" and follow the prompts to connect.
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/have-i-been-pwned).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of HaveIBeenPwnd** **stories in the [story library](https://www.tines.com/library?s=haveibeenpwnd&view=all)**

## HiBob Authentication Guide

HiBob built its HR management solution to help fast-growing companies attract, retain, and engage the modern workforce.

## First, get a HiBob access token

1.  Login to your HiBob account at [https://app.hibob.com/login/](https://app.hibob.com/login/)
    
2.  Click the username menu and select "API access"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/877828410/fcc955c9641479881409f7da/image.png?expires=1784851200&signature=a43753742c97edb472469fa6a9e20f7e35ce683e803e36301eb36dd7ac1edcc8&req=fCcgHst2mYBfFb4V1XW4gcvHeoL%2FbdPQNfZRbZXw2PbuQWuUIV85L3iH1ejj%0A545cKGWADHgH8Z1qTVnV4jK%2BJw%3D%3D%0A)
    
      
    ​
    
3.  Click "Generate token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/877829935/3abfc81f58563dc83c7bb7a2/image.png?expires=1784851200&signature=0e23810a94dd96384a54d2ee498213b941d9c4f8e1f54c572fd26443524ebfae&req=fCcgHst3lIJaFb4V1XW4gZOo%2BH%2B4aWhVbVu8ChhT1Ki77%2FGv%2FIXiaMLZRa%2FB%0AwHuKPK%2B4WNd3%2BjGSf6VXTWGvhw%3D%3D%0A)
    
      
    ​
    
4.  Copy the token to a text editor, choose the required scopes, and click "Save"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/877830765/641c3bc419d2ded950c1cccf/image.png?expires=1784851200&signature=6ac051656a908c322a272910214092dfa24e75a9d549dbf567ca4c59a869314e&req=fCcgHsp%2BmodaFb4V1XW4gdjY82Dmq%2FifEIEId3N2z18iWplfjI4A0fFFN3Cn%0A7D8hkROLNOqLm%2Bpj4v1dw%2B1MmA%3D%3D%0A)
    

## Lastly, create a HiBob credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the HiBob credential
    
    1.  **Name:** HiBob
        
    2.  **Description:** Optional
        
    3.  **Value:** Your access token
        
5.  **Optional**
    
    1.  **URLs and Domains:** Ensure this credential can only be used when making HTTP requests to specific domains. Example: https://api.hibob.com/v1/\* - This is not a clickable link but can be used in this section of the credential if applicable.
        
    2.  **User ID:** This can be saved in the credential under metadata
        
    3.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of HiBob** **stories in the [story library](https://www.tines.com/library?s=hibob&view=all)**

## Hints and Tips from the Tines Support Team

We recently asked our support team to share their tips and tricks for working in Tines, and here are their top ten hints!

Not sure where to find these features? Here's a quick visual guide to the four key sections of the storyboard:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332310252/8749b7b04874c3832ad2b4b6d2ae/Storyboard+UI.png?expires=1784786400&signature=9f12dc6bb2442d6fb052f37f701e1a31c153536c2a3f504f0bf22a29e0f077cb&req=diMkFMp%2FnYNaW%2FMW3nq%2BgWcOs9h2B2pz2D%2FAfOZaCBkDiyCr9FBTqt%2BUGa0o%0AzBzJ9oQ%2FAElVGT6m3sVWifby3nw%3D%0A)

1.  Use CMD/Ctrl K to search for stories, actions, resources, credentials, users, or notes. You can also search for Webhooks on your story using the Webhook secret, name, or path.
    
2.  You can copy curl commands and paste them into your storyboard by clicking into the blank space.
    
3.  Use CMD/Ctrl C to copy an action and CMD/Ctrl V to paste the action on your storyboard or chat window. You can also use CMD/CTRL to highlight multiple actions for copy/paste.
    
4.  Use CMD/Ctrl + e to open the event viewer.
    
5.  Use CMD/Ctrl + / to quickly close and reopen all the panels (left, right, and event viewer).
    
6.  Use Shift + click the Event pill to fully expand Event data (helpful when you have many Actions preceding so you don't need to expand all preceding Events in addition).
    
    ![](https://downloads.intercomcdn.com/i/o/936410199/4066f5a046643a2099b2cb39/Screenshot+2024-01-17+at+2.11.09+PM.png?expires=1784786400&signature=37c03019220fee8f775e5e7a7de5f2d62997c1cd0c4fb7e3b67653dfef8e225c&req=fSMhEsh%2BnIhWFb4V1XW4gXTNF6MB8HIkwzTrlMSWT%2F4d4Jg12Ep9izDzW%2FLj%0ADs1%2BB50c1%2FwcRyhei0WzdGheag%3D%3D%0A)
    
7.  Click into a blank space on your Storyboard and type ‘n’. This will add a [note](https://explained.tines.com/en/articles/7228541-how-to-create-a-note-in-tines). Once inside a note, type `/` to add toggle headings (H1, H2, or H3) — great for collapsing and expanding sections to keep your notes organized.
    
8.  The names of your actions are important. Use meaningful names so the events they create are easy to find and use further downstream in your story.
    
9.  Remember to configure [event retention](https://explained.tines.com/en/articles/7860183-how-long-does-tines-retain-event-and-log-data) in your story.
    
10.  To access data in an event in an event, open the event and double-click the key to copy the path that leads to that value. You can then paste this into a pill in the action you need that value in
     
11.  And finally...use templates! If you have an action you’re unsure how to create, go to the left of your Storyboard and click Templates. This will bring up all of our Action Templates. You can search for the tool or the API call you’re using, and we might have a template that you can then drag onto your Storyboard. Just add your credentials and information, then use it instead of building it yourself. (This can also be helpful if you’re having trouble formatting a call.)

## How are case IDs generated?

Case IDs are sequential integers; unique within each team. When a new case is created, the next available number in the sequence for that team is assigned. For example, Team A has cases `1`, `2`, `3`. A user creates a new case in Team B, it gets `case_id: 4`. The same case ID cannot exist across multiple teams in the same tenant.

> **Tines Tip**: Learn more about Tines cases [here](https://www.tines.com/docs/records-cases/cases/).

## How can I access audit logs in Tines?

To access the audit logs, select settings from your menu and choose 'Audit logs' in the 'Monitoring' section.

You may have to scroll through the settings options on the left.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1716961216/23aa73c15858861852e11e43a5cf/Audit+logs.png?expires=1784851200&signature=00c885d52252894921f4c5aab934e79b17690ede03c2fcd267258be5e6547b26&req=dScmEMB4nINeX%2FMW3nq%2BgRsJByciR7OGW%2BgWxpZF7XlcSQTMbFaJIhnJnhPZ%0AEIlTlf1GuIVaBU88F8UFMwQUVIY%3D%0A)

> **Important Note:** To access audit logs, you must be a tenant admin. Audit logs is part of the [Security Essentials](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging) Tines package.

## How can I check my self-hosted Tines version?

To check your version of self-hosted Tines, use https://yourtenanturl/version

> **👉 For more details on self-hosted Tines including how to update [see here](https://www.tines.com/docs/self-hosting/before-you-begin).**

## How can I check my  Tines self-hosted tenant's health?

You can use the following to check the health of your tenant. This applies to both self-hosted and cloud Tines: [https://yourtenant.tines.com/health](https://yourtenant.tines.com/health)

For self-hosted tenants, you can also check what data is being shared with TInes hourly by using the following: [https://yourtenant.tines.com/admin/telemetry](https://yourtenant.tines.com/admin/telemetry)

> **To learn more about self-hosted Tines telemetry, [see here](https://www.tines.com/docs/self-hosting/telemetry)**.

## How can I edit or delete private templates in Tines?

To access your private templates, click on your user menu → **Templates**:

![](https://downloads.intercomcdn.com/i/o/961081860/9434ca9c23cd5eb2a68421a2/Screenshot+2024-02-12+at+11.39.53+AM.png?expires=1784851200&signature=77970749d7ce898a3c90aba7a4b886bb21fa1f9bcfc569f8a12a8e4368d4ea53&req=fSYmFsF%2FlYdfFb4V1XW4gbjg6eF6KZ5XqaTcZUWmXwtgFKIcTzqSb37v5ZKF%0A%2FeI65ffgWfKRcAGV3O2pQHO9Cg%3D%3D%0A)

## Edit Private Templates

To edit a private template:

Within the **Templates** menu, click on the template you wish to edit to open the **Edit Template** window:

![](https://downloads.intercomcdn.com/i/o/961086616/94ff53913d05436f19e4c3df/Screenshot+2024-02-12+at+11.44.35+AM.png?expires=1784851200&signature=c760cb847bc059cc6db74efd801371d21765b3037136641e319230bfdbb499dd&req=fSYmFsF4m4BZFb4V1XW4gZAIjwNKu9Gbkw4TW8wfENNx8B01701w8jif5z%2F4%0A1P1fcEf9nL48uH9ShFuG2B57NQ%3D%3D%0A)

## Delete Private Templates

To delete a private template:

Within the **Templates menu**, click on the three-dot menu next to the template you wish to delete → **Delete**:

![](https://downloads.intercomcdn.com/i/o/961092189/873fa2affa88d0226cb8169c/Screenshot+2024-02-12+at+11.45.52+AM.png?expires=1784851200&signature=c86421f6158e63b5c9fb7fb5ce1d59f4a2afdaacb653116d1dd40242a3e26d84&req=fSYmFsB8nIlWFb4V1XW4geG67KgCY772uYcwBmLMPBXQHK%2FK5ht1HoHgPwg%2F%0A3ESALYoFVPU2L5UgErO7qEXa2Q%3D%3D%0A)

> **For more information on private templates, see [here](https://www.tines.com/docs/actions/templates/private-templates).**

## How can I get notified of new Tines features?

There are two RSS feeds you can subscribe to, depending on your preference.

1.  Important updates:
    
    1.  [https://www.tines.com/rss/tines-product-updates.xml](https://www.tines.com/rss/tines-product-updates.xml)
        
2.  All product updates
    
    1.  [https://www.tines.com/rss/tines-product-updates-full.xml](https://www.tines.com/rss/tines-product-updates-full.xml)
        
    

> **To view all the Tines feature updates, [see here](https://www.tines.com/whats-new).**

## How can I remove team members from a team?

You can remove team members from a team by navigating to your Dashboard, clicking the three dots adjacent to your team name and then selecting "Members".

Find the team member you want to remove, click on the member's team role adjacent to their name and then click the "Remove from team" button.

![](https://downloads.intercomcdn.com/i/o/953228700/f0ce18ebf521a5e9a963304c/image.png?expires=1784851200&signature=6d1a382cec0906c067d4f52a8c31b806b3267dec86223371309e550d81b2c0b3&req=fSUkFMt2moFfFb4V1XW4gRP8B%2BGgHzE0UTyJS8Jve9A0ztX69tJFHNjqR8Z3%0ArcBdZU0%2Fb5xKjEAhcHxkP74QGQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/953230644/c8455d800c0bc7f6cb75caee/image.png?expires=1784851200&signature=56e233773300a053655ca93b11ea98f6cb3a815cad3c89a65c0b86ec2d9e4cf6&req=fSUkFMp%2Bm4VbFb4V1XW4gV8nNgiKDhMoh4K2x%2BRlRYGSglP5qKy%2FAGwf9cIs%0Al%2BaGqa3%2BdeN5ens6zqp7n%2FGIbg%3D%3D%0A)

## How can I schedule an action in Tines?

You can schedule the following actions in Tines.

-   HTTP Request
    
-   Send Email
    
-   Event Transform
    
-   Send to Story
    

**Important Note:** To use them to schedule your story, ensure the scheduled action is the first action in the story flow.

To schedule an action, click the action and then "Status" and add the schedule as shown below.

![](https://downloads.intercomcdn.com/i/o/838118929/c599db81adf3d8777f1a839d/Screenshot+2023-09-25+at+10.47.46.png?expires=1784851200&signature=95a7ae71345eab904079aa9b824fe5b4886fb7fdc8030c2f29a4021793096dcc&req=fCMvF8h2lINWFb4V1XW4gdJVzlWs%2BIXfrYG1EhIH9naX9nkwDsAL%2BTKuP6CW%0AroZShqsbo7qms33a%2Bzbksp60lw%3D%3D%0A)

You can schedule the action by the minute, hour, day, week, month, or a cron expression.

![](https://downloads.intercomcdn.com/i/o/838120094/58ca07e829ce02eaec6e5dab/Screenshot+2023-09-25+at+10.52.51.png?expires=1784851200&signature=c43b1e3734d988391e2c12dbc0ac7bc7dfdec0ba69fa2c4109df1fbb30b42ec9&req=fCMvF8t%2BnYhbFb4V1XW4gdJH8RdNzgqs1fTw4zaMr%2B6oCWeZcOaV%2BUXUBU84%0A4bLmjGwwyx1%2BEWSCDV1q34oAYQ%3D%3D%0A)

> **To learn more about using actions in stories, [see here](https://www.tines.com/docs/actions).**

## How can I switch between tenants in Tines?

If you have multiple tenants, you can switch between them using the tenant switcher in your tenant admin menu in the top left.

![](https://downloads.intercomcdn.com/i/o/800345826/d05ff48efb49e0b425718357/9e8b1f2f-9499-4c12-8e45-822113b4b9c9.gif?expires=1784851200&signature=d3158c9b1dfca79b5065dcf7733753e6ac539d998588fc5c78e4d51d33439121&req=fCAnFc17lYNZFb4V1XW4gd%2FxGvwrWRwLMu3%2B2Y2jSaHBRH6dnAhrodcfhox%2B%0AaFzThv67QVIGdlaZsa9tHh9uPQ%3D%3D%0A)

## How can I track my published and unpublished stories?

Stories that are stored in "Your drafts" are not published. All stories that are published to a team are published. You can also [Tag](https://www.tines.com/whats-new/story-tags) your published stories.

![](https://downloads.intercomcdn.com/i/o/783884166/9cf052eb2b6e27c3ba861f43/Screenshot+2023-07-12+at+20.39.51.png?expires=1740915000&signature=ac73f435100ab729bdea4221908a8e7445604c50ba5b6a6a36353b6dafd0bbc1&req=cygkHsF6nIdZFb4f3HP0gNmraMqdOj6MGA7GW%2BxYGtT2m6hSkd4CHSV5tQ5X%0Aux0%3D%0A)

## How can I troubleshoot my credential metadata in Tines?

### **Overview**

When using a template or HTTP request action in Tines, you may encounter errors or null values if the credential is not properly configured. This occurs when actions attempt to reference credential data (such as INPUT.credential\_name.info.metadata or CREDENTIAL.service) but cannot access it because the credential is either missing, incorrectly linked, or inaccessible in your current workspace. This guide will help you identify the cause and configure your credentials correctly.

### **Cause and solution**

### **Cause one: Credential not linked to the action**

When you import a template from the Tines story library or add a template action to your story, the credential may not be automatically linked. This results in null values when the action tries to reference credential metadata using the INPUT key (e.g., INPUT.jira\_software\_credential.info.metadata.username) or when referencing the credential by CREDENTIAL.jira.

**Common error responses:**

-   **Null values returned for INPUT.credential\_name.info.metadata references**
    
-   **"Credential not found" errors**
    
-   **Authentication failures (401/403 HTTP errors)**
    
-   **Actions failing to execute due to missing authentication**
    

🟢 **Solution**:

1.  **Open the action in the Tines action editor**
    
2.  **Look for the credential field in the action configuration**
    
3.  **Click New connection or use the credential selector dropdown**
    
4.  **For templates with built-in connect flows: Follow the guided connection process to authenticate**
    
5.  **For HTTP request actions: Select an existing credential or create a new one**
    

Once properly linked, the credential metadata will populate correctly and the action will authenticate successfully.

### **Cause two: Credential exists in a different workspace**

Credentials in Tines are workspace-specific. If you created a credential in a Team space, it will not be available when working in your Personal space (and vice versa). This is a common issue when users switch between workspaces or collaborate across teams.

**How to identify this issue:**

-   **You can see the credential listed in one workspace but not in another**
    
-   **The credential dropdown appears empty when configuring an action**
    
-   **You receive "credential not found" errors despite knowing the credential exists**
    

🟢 **Solution**:

1.  **Verify which workspace you're currently in (check the team dropdown in the top left corner)**
    
2.  **Navigate to the Credentials page for your current workspace**
    
3.  **Check if the credential exists in this workspace**
    
4.  **If the credential is in a different workspace, you have two options:**
    

-   **Option A: Switch to the workspace where the credential exists and work on your story there**
    
-   **Option B: Create or share the credential in your current workspace:**
    

-   **If you have access to the original credential, you can share it across teams (requires Team Admin permissions)**
    
-   **Otherwise, recreate the credential in your current workspace**
    

1.  **Link the credential to your action following the steps in Cause One**
    

### **Troubleshooting checklist**

☑️ **Check credential linkage**: Open each action and verify a credential is selected in the credential field

☑️ **Verify workspace**: Confirm you're working in the correct Team or Personal space where your credentials exist

☑️ **Test credential access**: Navigate to the Credentials page and confirm you can see and access the credential

☑️ **Review credential metadata**: For templates, ensure any required metadata fields (like domain, username, etc.) are properly configured in the credential

☑️ **Check credential sharing settings**: If working across teams, verify the credential's Access settings allow sharing with your current team

☑️ **Replace credentials story-wide**: If multiple actions need the same credential update, use the story-wide credential replacement feature:

-   **For templates: Click on the credential field, select the current credential, then choose "Replace in story" to update all instances at once**
    
-   **For HTTP request actions: You can replace credentials at the story level, but you may need to manually update any CREDENTIAL formula references in headers, parameters, or body fields**
    

### **Need further assistance?**

If you continue to experience issues after following this guide, please open a support case with the following information:

-   **Story name and action name(s) experiencing the issue**
    
-   **Credential type (e.g., OAuth, HTTP Request, Text, JWT)**
    
-   **Workspace context: Which Team or Personal space you're working in**
    
-   **Error messages: Screenshots or exact text of any error messages received**
    
-   **Action configuration: Screenshots of the action's credential field and any relevant configuration**
    
-   **Expected behavior: What you're trying to accomplish with the credential**
    
-   **Steps taken: Which troubleshooting steps you've already attempted**

## How do I add a tenant owner in Tines?

**Important Note:** A Tenant owner is required to create another tenant owner. If the existing tenant owner is unavailable, [contact](https://www.tines.com/getting-answers-to-your-tines-questions) us—we’re here to help.

Select the menu at the top left.

![](https://downloads.intercomcdn.com/i/o/1159498558/bcf4be62134f779c51e00b6d/Screenshot+2024-08-27+at+09_42_04.png?expires=1784851200&signature=50ce726364e2ea236f601316466085eb3eb07d11934423aae9827d2148c4a7ad&req=dSEiH813lYRaUfMW3nq%2BgU%2FCQmxPdnkTSn8ka9%2FUOQV08aqXTfVC2S%2FJQXBN%0AURRndfDMqWDXmVRrlV6gr4Og4dg%3D%0A)

Select "Users" and double-click on the user you wish to make an owner. Toggle the "Tenant Owner" button as needed.

![](https://downloads.intercomcdn.com/i/o/1159501580/d74c0e1dfaa0159b981b0a09/Screenshot+2024-08-27+at+09_44_52.png?expires=1784851200&signature=571adfb79e58be88ba655d6a11f918c6a5e350dca97c021f45a3d5ea253278cf&req=dSEiH8x%2BnIRXWfMW3nq%2BgZrqA0IhuqqIRO2Uz4vWhdfj3tzu6W0HMeh4tByk%0AY3e%2FKwuMGWevppsFr%2FkwnzAEc70%3D%0A)

## How do I back up my Tines stories?

You can export your stories from Tines, which will be stored in a JSON format.

For more information on exporting stories, [see here](https://explained.tines.com/en/articles/7228646-how-to-export-stories-and-actions-in-tines).

You can also use the Tines API to export stories. For more information on exporting stories using the TInes API, [see here](https://www.tines.com/api/stories/export).

Here are some stories that can be used to back up, export, and import Tines stories.

-   [Backup Tines stories to Github](https://www.tines.com/story-library/87728/backup-tines-stories-to-github)
    
-   [Save Tines story in AWS S3](https://www.tines.com/story-library/87683/save-tines-story-in-aws-s3)
    
-   [Import Tines story from AWS S3](https://www.tines.com/story-library/87682/import-tines-story-from-aws-s3)

## How do I disable SSO on my self-hosted deployment of Tines?

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.

> **👉 To learn more about SSO, see [here](https://www.tines.com/docs/admin/single-sign-on).**

## How do I make a user an admin in Tines?

To make a user an admin, select the three dots next to your team name on your Tines homepage. Next, select the user and their current role, and you will be presented with the option to make the user either an admin, editor, or viewer.

![](https://downloads.intercomcdn.com/i/o/730837296/aea764f5758032bb8723dcf7/Screenshot+2023-04-28+at+16.19.27.png?expires=1784851200&signature=12dc64b197f914d454b241c892d7f9e3009ae5ed1cefe7e4c3aaa32d341d46ba&req=cyMnHsp5n4hZFb4V1XW4gbeUpM7c2Xi464TGR6LPlMK5EwlucQ35RB5e3X%2Br%0ApJzusS1pjbd9JWqAJw70kVGQ%2Bg%3D%3D%0A)

> **👉 For a more detailed description of teams and roles [see here](https://explained.tines.com/en/articles/6883947-using-teams-in-tines).**

## How do I report a vulnerability to Tines?

We do not have a bug bounty program with compensation in place. However, we do have a vulnerability disclosure program via BugCrowd.

Vulnerabilities can be submitted to the program directly here: [https://www.tines.com/security#reporting-security-vulnerabilities](https://www.tines.com/security#reporting-security-vulnerabilities).

Please let us know if you have any issues with the report, please email security@tines.com

## How do I report possible security vulnerabilities to Tines?

Please follow the link provided below and follow the instructions in the submission form. This will be routed directly to our security and compliance teams.

[https://www.tines.com/security#reporting-security-vulnerabilities](https://www.tines.com/security#reporting-security-vulnerabilities)

## How do I request the deletion of my Tines tenant and data?

To request deletion of your Tines held data, just complete [this form](https://www.tines.com/gdpr-request). Please provide the tenant URL.

> **Note:** Only the tenant owner can request a tenant to be deleted.

## How do I schedule my story?

Only stories published to a team can be scheduled. To publish a story, move the story from your drafts to one of your teams.

To schedule a story, click on the first action and click "Status". The schedule option will be available as shown below.

![](https://downloads.intercomcdn.com/i/o/730608536/793d3b4717d6f4bbdecc22e5/Screenshot+2023-04-28+at+11.59.17.png?expires=1784851200&signature=4596a3598e6abaeee2f1ecbd12bae1bd75ce7430057dfeb414aef53b7ecdcff7&req=cyMnEMl2mIJZFb4V1XW4gQd8jr6Dcpk0%2BjF%2B7qLT0hyFnKsLLW7mYeI2L7Pm%0AwvdXOYuDTmAE9GWoCGyR4g9Gsw%3D%3D%0A)

**Important Note:** Actions inside a [group](https://www.tines.com/docs/stories/groups) cannot be scheduled. Groups can be scheduled to run.

## How do I switch between Light and Dark modes?

To switch display modes in Tines, click on your name on the top left of the screen, and select "Light Mode" or "Night Mode".

![](https://downloads.intercomcdn.com/i/o/729758612/5842a06eb2596f40571f317d/Screenshot+2023-04-27+at+11.33.16.png?expires=1784851200&signature=e9e67012f953d175b75ccdec5640ddd64efd320ca199aec477c3cb66e16c9abb&req=cyIuEcx2m4BdFb4V1XW4gan6t4Obzl9zZ5bTo42LJzoKON7GyccxDdAGGC4G%0Ao%2FbgFjPRSg2Tt3K7a2ioUPJFlA%3D%3D%0A)

## How do I turn on or off the AI-powered features?

We believe the AI-powered features are extremely powerful in improving your overall efficiency as builders and problem solvers. While we hold firm on that belief, we also understand that your company might have policies or processes for opting into AI features.

To allow for this, the tenant owner can turn off or on the AI-powered features tenant-wide. They can also allocate AI Agent action credits to different teams in their tenant to manage their usage.

### Turn on or off

Follow these steps to turn on all AI-powered features:

1.  Go to the **Settings** drop-down in the top left of your screen.
    
2.  Choose AI Settings.
    
3.  Check the box to turn AI on; uncheck it to turn AI off.
    

[Watch video](https://www.youtube.com/embed/GaS_bvNI7QQ?rel=0)

#### Automatic mode in the Event Transform action

Automatic mode in the Event Transform action is available to all plans and does not require credits or team allocation. After turning it on, it's available to all teams in your tenant.

### Distributing credits for the AI Agent action

The AI Agent action requires you to distribute credits in addition to turning the feature on. To allocate credits to one or multiple teams, follow these steps:

1.  Go to the AI settings screen
    
2.  Check the box next to the team(s) who should have credits
    
3.  Specify the credit volume for each team
    
4.  Click 'Save distributions'
    

[Learn more about AI Agent action monthly credits here](https://explained.tines.com/en/articles/9369092-how-ai-usage-and-ai-credits-work-in-tines).

## How do I update my license key on my Tines self-hosted install?

Important Note: Only a tenant [owner](https://explained.tines.com/en/articles/6885158-user-administration-in-tines) can set the license on your tenant.

1.  Visit [https://your-tenant-url/admin/license](https://your-tenant-url/admin/license)
    
    ![](https://downloads.intercomcdn.com/i/o/744379861/86025d516fc22e112e9ed6d4/10d75402-aa5e-473e-b546-2cd6564f8444?expires=1784851200&signature=367e3bd785d4e4634d856d095fd0404ba6eddf8b592b98d1df76569db5a3154f&req=cyQjFc53lYdeFb4V1XW4gXs5OXI9AHjvXHZ5A7cRBYLXuvjHXgee6PQ%2BZTwD%0AemL4dS%2BOcWAyRpVNFFj3KbK6hQ%3D%3D%0A)
    
2.  Paste in your license key provided by TInes and click “Validate License”
    

![](https://downloads.intercomcdn.com/i/o/744380051/526403cd9dac34ba7fa9bf4d/96152aaf-1860-48aa-9cce-56ed6c259708?expires=1784851200&signature=e7e0b5bb872bb0636ad89d8d52856b9baf7641e51eb1602331b933c33c307502&req=cyQjFcF%2BnYReFb4V1XW4gQNZ9BUQaJgpuPk6V9qcuE0WrbfsCF0lQkJMv6Xi%0A27kiAoHDQyJwd3PiNZHObq12qQ%3D%3D%0A)

3\. Ensure that all details on the validation page are correct. Correct values match the features and limits you purchased. Please ensure that the license is being applied to the right Tines environment.

  
​

4\. Click “Save New License” to save the license and apply your tenant features.

![](https://downloads.intercomcdn.com/i/o/744380316/fe1e0c2452601f0e87411684/16b23677-34dc-4b93-9806-b074b56d198e?expires=1784851200&signature=5646755fc711f066db178301eac6237ba7ca8dbe37c2fae07467b88f2de77d6d&req=cyQjFcF%2BnoBZFb4V1XW4gZ1pGCVfIy%2BdGd1XA1%2BwIAjKwkxdeTXodFsWve4M%0AFnthjn13KcxXzgzZx92RahxHKQ%3D%3D%0A)

## How do I use time saved reporting?

Time saved can be configured at both the [action](https://www.tines.com/docs/actions/reporting) and the [story](https://www.tines.com/docs/stories/time-saved) level.

**Important Note:** Time saved can only be used for stories published to a team.

## How long does Tines retain event and log data?

By default, Tines retains event and log data for seven days. **Depending on your Tines subscription**, you can configure how long event and log data are stored in Tines. Open your story, and on the right panel, select the desired option from "Keep events and logs."

![](https://downloads.intercomcdn.com/i/o/733849449/d0ed06e0cb53f8bbbf596df6/Screenshot+2023-05-03+at+15.22.50.png?expires=1784851200&signature=1134e6d5e46c1fb146fff27a2e45b47be880f09497d7d9527f61388b9bf631c5&req=cyMkHs13mYVWFb4V1XW4gf%2BnS59Ws5bcKXkUK%2FVKmBrlgTV5jB2l%2ByTH%2BGHp%0ABHGBHXSDMSj4gc%2F3ffT1uzTd5w%3D%3D%0A)

Then select how long you would like to store the data for:

![](https://downloads.intercomcdn.com/i/o/733850060/764c351716299d0c52555b09/Screenshot+2023-05-03+at+15.23.13.png?expires=1784851200&signature=77f5fd4e95f543b24e385e4b8b28276d774caf73e940cffe101308fa308a827b&req=cyMkHsx%2BnYdfFb4V1XW4gUoARRtC3SWZWgvxh6aWAH5D9eSLo%2FjDaqh9a%2BXt%0AED%2B791ubu8APUqSzzqConJ1GoA%3D%3D%0A)

## How to access Workbench

Workbench is a Tines-powered AI chat interface that empowers builders like you to take action and interact with your proprietary data in real-time. Use this breakdown to understand which version of Workbench is right for your team. To learn more about Workbench, [check out this article](https://explained.tines.com/en/articles/9822635-introduction-to-workbench).

Workbench availability is tied to your AI-powered features. If you’ve [opted into these features](https://explained.tines.com/en/articles/9396326-how-do-i-turn-on-or-off-the-ai-powered-features), there are two ways for you to access Workbench:

-   Free: 20 daily messages; available to tenant admins only
    
-   Paid: up to 100 Workbench users, unlimited messaging, and fully featured version
    
    -   Paid includes a full-access trial opportunity as described below
        

### Workbench for all

All tenant admins can see Workbench on their tenant, even community edition tenants. They can freely interact with Workbench for up to 20 daily messages tenant-wide.

##### **Chat history**

Chat history is stored for different periods between the community and paid editions. See details below:

| Community edition | 1 day |
| --- | --- |
| Paid editions | 7 days |

### Workbench add-on

To get the most value out of Workbench, you can purchase the full version of Workbench. This add-on includes:

-   Access for **all users** in the tenant
    
-   Chat history retained for up to 2 years
    
-   Unlimited tenant-wide messages per day
    

#### Workbench full access trial

We offer a 21-day full-access trial to paid edition tenants. This is a great way to try it out in a full-access trial opportunity.

##### **How to get started**

Any tenant admin can opt into the Workbench trial directly within their tenant or via [this form](https://hq.tines.io/pages/workbench_trial/). Once you complete it, your Tines account team will reach out to kick things off.

Throughout the trial period, your dedicated Tines team will be available to support any questions directly or through the in-app messenger.

Once the trial ends, all non-admin users will lose access to chat with Workbench unless you’ve purchased the add-on.

### Making the case for Workbench

Not a tenant admin, but think Workbench is right for you? Reach out to your tenant admin to request the trial for your organization. If you need some quick points to make the case, consider these:

-   Increase operational efficiency
    
-   Improve the interoperability of your tech stack
    
-   Mitigate risk faster
    
-   Maintain compliance better
    

We recommend sharing links to our [AI in Tines security and privacy article](https://explained.tines.com/en/articles/9369078-security-privacy-for-ai-in-tines) alongside those bullets to pre-empt related questions.

## How to add stories for Workbench

In this video, we'll learn how we can add a story to be accessible by Workbench.

[Watch video](https://www.youtube.com/embed/Kj-e3CLjngM?rel=0)

## How to build APIs

You can create powerful workflows by connecting tools and processes together - all without any code at all.

## Build an API

You can do this following the familiar process of building a workflow in Tines.

1.  Enable send to story on your workflow.
    
2.  Choose the option to “return data from the exit action as a HTTP response.”
    
3.  That's it.
    

**Here are some examples to help you get started:**

-   **[Create an IP enrichment API](https://www.tines.com/library/stories/1173034/?name=create-an-ip-enrichment-api)**
    
-   **[Create and manage todos](https://www.tines.com/story-library/1158535/create-a-todo-list-api)**
    
-   **[Manage credits for ChatGPT](https://www.tines.com/library/stories/1170948/?name=manage-user-access-to-chatgpt)**
    
-   **[Redirect requests based on browser platform](https://www.tines.com/story-library/1168201/redirect-requests-based-on-browser-platform)**
    

### **We made our own free APIs for you to use**

We often get feature requests for easy access to utilities like WHOIS inside Tines. Now, rather than needing to find homes in the product for requests like this, we can build APIs that Tines tenants can easily connect to – without any manual authentication.

Here are four examples"

1.  Fetch WHOIS information
    
2.  Generate a screenshot for a URL or HTML
    
3.  Find domains for an IP (a.k.a., reverse IP)
    
4.  Find IP Addresses for a domain
    

> Learn more the Tines API toolkit [right here](https://www.tines.com/blog/introducing-the-tines-toolkit) 👈

Lastly, let us know what APIs you'd like us to build next. We'd love to hear from you.

## How does Tines handle API retries?

With the HTTP request action, you can use the "Retry on status" or the "Retries" options available for the action.

Select the HTTP request action and click "+ Option" to set these.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2306192310/0980a60471cbff8931fb695f65ac/retries.gif?expires=1784786400&signature=2dab9fbdb42460e63099c51c1b74469062a3b5884ff7106118c43de003fd6d70&req=diMnEMh3n4JeWfMW3nq%2Bged7Pl3LdOo2BmxpJrPh6JZ7CAAmAMl%2FgXr76ICr%0AHz2wQGxkWF%2F%2BuD7EMZQUfnwFmf4%3D%0A)

This lets you choose how many times to perform a retry on the HTTP request action if it fails the first time. Leverage the "retry on status" to determine what status codes it should attempt a retry - like 429. 429 is the status code for a service being busy.

When "retry on status" is enabled, Tines will silently attempt a retry up to 25 times, but this figure of 25 can be reduced using the "retries" field.

## API keys automatic expiration

Tines API keys do not have an auto expiration.

> **To learn more about creating a Tines API key, [see here](http://tines%20api%20keys%20do%20not%20have%20an%20auto%20expiration./).**

## How to chat with Workbench

In the below video, we'll take a look at how we can interact with Workbench.

[Watch video](https://www.youtube.com/embed/lx2XeedEcCk?rel=0)

## How to configure an HTTP request in Tines

This article will guide you through configuring an HTTP request action, detailing the three main components and walking through an example of retrieving cat pictures using an API.

## Key components of an HTTP request

There are three main components to be aware of when configuring an http request:

-   **URL endpoint**: This is the service you are reaching out to, essentially asking that service to perform some kind of task.
    
-   **Method**: This determines the specific action you want the service to perform.
    
    -   `POST` is used to ask the service to do something on your behalf, such as asking slack to send a message.
        
    -   `GET` is used for retrieving information, such as getting all of the slack channels in an environment.
        
    -   `PATCH`: Is used to send data to perform a partial update on existing data, such as updating only a user's phone number in a directory.
        
    -   `PUT`: Is used to send data to replace an entire existing data record, such as replacing all the details of a contact record in a service.
        
    -   `DELETE`: Is used to remove data, such as permanently deleting a specific message from a chat service or a user account.
        
-   **Payload**: This includes any additional criteria on how you make the request, such as specifying that you want the first 10 users in a slack channel, or all the users.
    

## Example: Retrieving cat pictures

To demonstrate a simple configuration, we will reach out to a service to get random cat pictures. Here is a video of this example with the steps involved below:

[Watch video](https://www.youtube.com/embed/L-cvjnzpFII?rel=0)

### Steps involved

-   **Rename the action**: Rename the http request action to "get cat picks".
    
-   **Configure the URL endpoint**:
    
    -   Go to [developers.thecatapi.com](http://developers.thecatapi.com)
        
    -   Under the quick start section, copy the text for getting a random image: [`https://api/thecatapi/v1/images/search`](https://api/thecatapi/v1/images/search)
        
    -   Paste this text into the URL field.
        
-   **Set the method**: Since the request is trying to get information, switch the method to `GET`.
    
-   **Remove the payload**: Delete the payload since it is not needed for this request.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244528680/701042c0f5be576c81490715600b/4cd52b75-e9aa-4cea-ba52-719b7cbea8ae?expires=1784851200&signature=4aa39ab67440312fc41855833b0efc0c7a202a1aa0d1e02e22e7f88c9d9a7803&req=diIjEsx8lYdXWfMW3nq%2BgZtQLsgsxlkhX%2FYlaKdyJc6Sg6WlNwHBBwpVbRVe%0AK7NW0zsI2kk3pedzLNunHPQ7CDQ%3D%0A)

-   **Run the request**: Hit run to execute the configuration.
    
-   **View the result**:
    
    -   Once the run is successful, locate the "get cat picks" action in the events section.
        
    -   Expand the three dots, then expand the three dots for "body," and then expand the three dots once more.
        
    -   The result will provide an id, a URL, a width, and a height.
        
    -   Copy the URL and open it to view the cat picture.
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244531307/6dc8091063f61e39fd9598fa8f01/cat+pics+example.gif?expires=1784851200&signature=9a43fe59be15b6783e6eec2c91cc638b70f00f7d82118e462f77ea76d5e677ab&req=diIjEsx9nIJfXvMW3nq%2BgQME5effSZl0o%2Fan2OizYRgOqVMCe8m1ElV%2FGk6d%0AemQdNWQn5jgnUH3yvRuLGzZysFc%3D%0A)

This example shows how simple it can be to configure your first http request.

> Note that as requests become more complicated (e.g., requesting the service to delete.

> **👉 For a more detailed description of HTTP requests** [see here](https://www.tines.com/docs/actions/types/http-request).

## How to connect SaaS Tines to your on-premise resources

## Overview

Not every system your team needs to automate lives in the cloud. Whether it's an on-prem active directory server, an internal ticketing system, or a legacy API behind a firewall, the reality for most organizations is a hybrid landscape.

The good news: you don't need to self-host Tines to reach those resources. A handful of lightweight containers, deployed in your network, bridge the gap.

## The building blocks

These three container types cover every on-prem connectivity scenario:

-   Tines Tunnel
    
-   Tines Command Runner (TCR)
    
-   Command-over-HTTP (CoHTTP)
    

### Tines Tunnel

This enables HTTP Request actions to reach private services. No inbound firewall rules required. You only need to deploy a single container inside your network.

#### Best used for

Tines tunnel is best used for connecting to on-prem HTTP APIs such as Jira Server, GitLab or internal REST services.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2230422292/85c4d0e52f39db049e08df121dcc/image.png?expires=1784851200&signature=39091e2897b7882edb2ab48d8dd700002051157feac4c126e08055cc301e7ffb&req=diIkFs18n4NWW%2FMW3nq%2Bgf6KUCPx7pGccWcxO8jdWTri%2ByPE%2FowYfjFoCjqM%0AycFGLDsPt%2Bc8KL7PuEqRL%2BvPxv0%3D%0A)

#### Key benefits

Tines Tunnel is a secure, outbound-only connection from your network to Tines via Cloudflare Tunnels. It is secure as there is no need for a VPN and there aren’t any exposed ports.

#### Minimum requirements

1 vCPU, 1 GB RAM. For HA, run a second instance on a separate host with the same secret.

> Read this article on [deploying and using the Tines Tunnel](https://explained.tines.com/en/articles/6883952-deploying-and-using-the-tines-tunnel).

### Tines Command Runner (TCR)

TCR powers the Run Script action on your infrastructure. It runs Python with access to packages like \`requests\`, \`pandas\`, \`cryptography\`, and \`boto3\`. Adding libraries is straightforward, just point TCR at a public or local PyPI server and install what you need.

#### Best used for

Use TCR for complex transforms, custom enrichment, SDK integrations, or anything that goes beyond Tines formulas. You can also combine TCR with the Tines tunnel: the tunnel handles HTTP Request actions to internal services; TCR handles Run Script actions.

#### Key benefits

TCR executes Python in isolated, per-team sandboxes with 60+ pre-installed packages. It pairs with an optional local PyPI server for air-gapped environments.

#### Minimum requirements

2 CPUs, 2 GB RAM recommended. Optional \`pypi-server\` for local package caching.

### Command-over-HTTP (CoHTTP)

Executes Python, Bash, and PowerShell on your private network. Built for systems without HTTP interfaces, with native Kerberos authentication for Active Directory and Exchange.

#### Best used for

Automating active directory user management, Exchange mailbox provisioning, or domain controller queries. In your workflow, target \`[https://command-over-http/run](https://command-over-http/run)\` with a JSON payload specifying the executor and script, and select a tunnel on the action to route the request into your network.

> **Note**: CoHTTP's library set is fixed — if you need custom Python dependencies, TCR is the better fit.

#### Key benefit

CoHTTP is purpose-built for systems without HTTP APIs. CoHTTP accepts script payloads and executes them using Python, Bash, or PowerShell — with Kerberos authentication out of the box.

#### Minimum requirements

1 vCPU, 1 GB RAM.

### The deployment matrix

The following matrix summarizes each of the 3 building blocks listed above.

| Pattern | Containers | Best for | Key benefit |
| --- | --- | --- | --- |
| Tunnel | Tines tunnel | On-prem HTTP APIs (Jira Server, GitLab, internal REST services) | Secure, zero-inbound connectivity |
| TCR | \`tines-command-runner\` + \`pypi-server\` | Python scripting (data transforms, enrichment, custom logic) | 60+ packages; isolated per-team execution |
| CoHTTP | command-over-http | Active Directory, Exchange, PowerShell/Bash | Native Kerberos auth; no HTTP interface needed |

Use these links to learn more about the optimal way to connect Tines to your on-premise resources:

-   [Tines Tunnel](https://www.tines.com/docs/admin/tunnel/)
    
-   [Command-over-HTTP](https://www.tines.com/docs/admin/command-over-http/)
    
-   [Run Script / TCR](https://www.tines.com/docs/self-hosting/additional-applications/run-script-for-self-hosted/)
    
-   [Deployment options overview](https://www.tines.com/blog/understanding-your-tines-deployment-options/)

## How to connect ServiceNow to Tines Webhooks

Integrating ServiceNow with Tines allows you to automate workflows by sending real-time event data from your ServiceNow instance directly into your Tines stories. This article will guide you through the process of setting up ServiceNow webhooks using outbound requests.

For more information on creating webhooks, read our docs [here](https://www.tines.com/docs/actions/types/webhook/).

## Create a webhook in Tines

Within a Tines story, drag a webhook action onto the canvas. Name and copy the webhook URL for later.

Read this article for best practice on [configuring your webhook names and paths](https://explained.tines.com/en/articles/14048386-webhook-best-practice-configuring-names-and-paths).

## Create a REST message in ServiceNow

Open your ServiceNow instance <<https://tenant-name.service-now.com/>>.

Open the “All” tab and navigate to “System Web Services” > “Outbound” > “REST Message”

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192606929/9f1eed53545e80d05ea91aa549e3/d69c937f-4b2c-484a-b29c-39765f190e29?expires=1784851200&signature=036001fe1fc52ea315ed3e0908f50112533888fa28ad4f0ed3278de226e5fb68&req=diEuFM9%2Bm4hdUPMW3nq%2BgSdKrvAng9%2F5W4OZMwU0WuO67GWGIrDfDNGtgH9b%0A2ZVrcCMI4qlT2uTETMqjxJHjbas%3D%0A)

Select “New” to create a new REST Message.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192608149/3342f038f635f7178f8c10256304/036b940a-0fbe-45e1-8c33-b7d941de5e98?expires=1784851200&signature=bc3f09a2a3822026fc21bbb6d861ae2b1ea01e47019c592b2b4f866abf2649b4&req=diEuFM9%2BlYBbUPMW3nq%2BgdfUZNFu9nnNohdSEXAytOlyLgsWWNUzc6jRRrlc%0Aj0e40%2Bk4I2MftG2Wqo7iTAXbvqY%3D%0A)

Name the REST message and paste in the Tines webhook URL from earlier. When finished, select “Submit”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192608148/1fe5c545156b352046584eec2ccd/8c83b79f-ef04-4f2c-be98-17f02989c930?expires=1784851200&signature=2a936081dde72e0d456b26fdea35d4f020794c19b76bb5c2d3feb22970188d09&req=diEuFM9%2BlYBbUfMW3nq%2BgabSZXruyRFOVxpnLgCsdHH6mwUueNHDcgrCcCkv%0ASaq6M8x72yRorz4yyIr0xiQszuk%3D%0A)

## Create a HTTP method

From the list of REST message names, select the one you just created. Scroll to the bottom and navigate to the HTTP methods section. There will be one default method already created called “Default GET”.

> Note: Delete this HTTP method

Create a new method for PUSH requests. Select “New”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192612685/b238ffcfab37647085bae52ffa34/265315b7-b9d7-465b-bdd6-b6c12503927a?expires=1784851200&signature=1b9098ea8b9856af5dd57f3f7cbc993b8f3e93779e8ef955fcdc71024de32f2d&req=diEuFM9%2Fn4dXXPMW3nq%2BgQNlJKzDMX5di%2Be0PohefUjqXSvqS7%2FYHVeS8hOP%0A8mzK%2BwLkLNzXSJ4GnGwpdl1of5I%3D%0A)

Name the new HTTP method something like “Post to Tines Webhook” and set the HTTP method to POST. Set the endpoint to the URL from your Tines webhook and click “Submit”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192612686/8b0d56e4d5c13fbbb0874f3c375f/e9c0eb0c-f837-4bc3-b4d8-4d051780e168?expires=1784851200&signature=10284a67c0fd2c0be0c19c8859a5913e5bef21a48b13bc2d655100aeadffdd04&req=diEuFM9%2Fn4dXX%2FMW3nq%2BgRSTzXO%2BoztA7rA%2F5LwILAe00WOlJQwyomqdhYXA%0ACB7Hc24BzSeJf%2FpKNyKf4c4zzZQ%3D%0A)

On the HTTP Request tab, note the Content of the HTTP Query Parameters section. This is where you can specify the fields that you want to be sent over to the webhook. The following would be used for the incident table in ServiceNow, but you can customize based on your needs. Select “Submit” when finished.

```
{
  "parent": "${current.parent}",
  "made_sla": "${current.made_sla}",
  "caused_by": "${current.caused_by}",
  "watch_list": "${current.watch_list}",
  "upon_reject": "${current.upon_reject}",
  "sys_updated_on": "${current.sys_updated_on}",
  "child_incidents": "${current.child_incidents}",
  "hold_reason": "${current.hold_reason}",
  "origin_table": "${current.origin_table}",
  "task_effective_number": "${current.task_effective_number}",
  "approval_history": "${current.approval_history}",
  "number": "${current.number}",
  "resolved_by": "${current.resolved_by}",
  "sys_updated_by": "${current.sys_updated_by}",
  "opened_by": "${current.opened_by}",
  "user_input": "${current.user_input}",
  "sys_created_on": "${current.sys_created_on}",
  "sys_domain": "${current.sys_domain}",
  "state": "${current.state}",
  "route_reason": "${current.route_reason}",
  "sys_created_by": "${current.sys_created_by}",
  "knowledge": "${current.knowledge}",
  "order": "${current.order}",
  "calendar_stc": "${current.calendar_stc}",
  "closed_at": "${current.closed_at}",
  "cmdb_ci": "${current.cmdb_ci}",
  "delivery_plan": "${current.delivery_plan}",
  "contract": "${current.contract}",
  "impact": "${current.impact}",
  "active": "${current.active}",
  "work_notes_list": "${current.work_notes_list}",
  "business_service": "${current.business_service}",
  "business_impact": "${current.business_impact}",
  "priority": "${current.priority}",
  "sys_domain_path": "${current.sys_domain_path}",
  "rfc": "${current.rfc}",
  "time_worked": "${current.time_worked}",
  "expected_start": "${current.expected_start}",
  "opened_at": "${current.opened_at}",
  "business_duration": "${current.business_duration}",
  "group_list": "${current.group_list}",
  "work_end": "${current.work_end}",
  "caller_id": "${current.caller_id}",
  "reopened_time": "${current.reopened_time}",
  "resolved_at": "${current.resolved_at}",
  "approval_set": "${current.approval_set}",
  "subcategory": "${current.subcategory}",
  "work_notes": "${current.work_notes}",
  "universal_request": "${current.universal_request}",
  "short_description": "${current.short_description}",
  "close_code": "${current.close_code}",
  "correlation_display": "${current.correlation_display}",
  "delivery_task": "${current.delivery_task}",
  "work_start": "${current.work_start}",
  "assignment_group": "${current.assignment_group}",
  "additional_assignee_list": "${current.additional_assignee_list}",
  "business_stc": "${current.business_stc}",
  "cause": "${current.cause}",
  "description": "${current.description}",
  "origin_id": "${current.origin_id}",
  "calendar_duration": "${current.calendar_duration}",
  "close_notes": "${current.close_notes}",
  "notify": "${current.notify}",
  "service_offering": "${current.service_offering}",
  "sys_class_name": "${current.sys_class_name}",
  "closed_by": "${current.closed_by}",
  "follow_up": "${current.follow_up}",
  "parent_incident": "${current.parent_incident}",
  "sys_id": "${current.sys_id}",
  "contact_type": "${current.contact_type}",
  "reopened_by": "${current.reopened_by}",
  "incident_state": "${current.incident_state}",
  "urgency": "${current.urgency}",
  "problem_id": "${current.problem_id}",
  "company": "${current.company}",
  "reassignment_count": "${current.reassignment_count}",
  "activity_due": "${current.activity_due}",
  "assigned_to": "${current.assigned_to}",
  "severity": "${current.severity}",
  "comments": "${current.comments}",
  "approval": "${current.approval}",
  "sla_due": "${current.sla_due}",
  "comments_and_work_notes": "${current.comments_and_work_notes}",
  "due_date": "${current.due_date}",
  "sys_mod_count": "${current.sys_mod_count}",
  "reopen_count": "${current.reopen_count}",
  "sys_tags": "${current.sys_tags}",
  "escalation": "${current.escalation}",
  "upon_approval": "${current.upon_approval}",
  "correlation_id": "${current.correlation_id}",
  "location": "${current.location}",
  "category": "${current.category}"
}
```

## Create a business rule in ServiceNow.

A business rule gives the REST message the context you want sent over to Tines.

In your ServiceNow instance, open the “All” tab and navigate to “System Web Services” > “System Definition” > “Business Rules”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192621664/ee1734a9f6b499ef118bff2364fe/28188dae-e5df-4d22-8a05-561fc0928cd3?expires=1784851200&signature=6e2c1791433961dbf2c277c824cf5e481f31ab22232781347061608ef5005000&req=diEuFM98nIdZXfMW3nq%2BgW28UdfaOJw%2BadshTXO5LDUqGNcmseX8EWcOyRPD%0APb3Q0wIgARlccZuLf87HDVE7evc%3D%0A)

Select “New”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192622958/f0648496819439d2fd367b5a17cb/afa7143d-c86f-40e5-b501-c3c6e38df682?expires=1784851200&signature=5f85245e359a5ff1c2185e2c360f35f300966b5c2dc19e62679878b2380cbf26&req=diEuFM98n4haUfMW3nq%2BgbxuezQNL1h7qtxHHorkMw9qFzLiSxqoNbIxh4nH%0AAHW%2FUVHR4ianeaDkhOSnCuSzx84%3D%0A)

Name the business rule something relevant to the data you will be sending to Tines and select the table from within ServiceNow that your data will be coming from.

In this example, we have “Tines Webhook” and “Incident \[incident\]” respectively. Toggle on the “Advanced” check-box.  
​

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192623285/f4a42842090f1a9f2bef0896f0c7/d5e24e85-3813-4345-b9e2-949a9a5737d6?expires=1784851200&signature=a5164e972f98abb841ce71d5632fda09e82c96ce0999ca262cbc888fbc44aca1&req=diEuFM98noNXXPMW3nq%2BgQzYpPj8gq2Q%2BGMESslJxhxkRg042u2Xoz%2Bn7HdM%0A%2F5PQrmwd6r%2Faud4vMBjBB2bqNd8%3D%0A)

The next steps depend on when you want the Tines webhook to receive data. Depending on your use case you may wish for the business rule to run when there is an ‘Insert’ or ‘Update’ to the underlying data. You may also want to specify filter conditions under which the business rule should run.

In this example, we specified ‘Insert’ only.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192627129/391268f6f250ec6e58e68a93cab9/604bb522-5a2a-41c9-b1e0-505f8140c356?expires=1784851200&signature=fdebe864293c1aa2489242be69e6285ecdd84304ec99ba4c9be72536246a9ce4&req=diEuFM98moBdUPMW3nq%2BgZISm7FO%2F%2BHW8puEsoIVQ5HiGjy4OfK94DvKMxOO%0AyLqUtM0V%2BXqzbvgZyrarwL9BWA8%3D%0A)

In the Advanced tab, you can also include a script. Paste in the following or something similar to use the fields that you’ve specified in your REST message.

> Note: If using this script, make sure to modify the Tines webhook URL with the URL from the one you created earlier.

Select “Submit”.

```
(function executeRule(current, previous /*null when async*/) {
    try {
        // Create the payload to send to Tines
        var payload = {
        };
        
        // Add relevant record fields to the payload
        payload.number = current.number.toString();
        payload.sys_id = current.sys_id.toString();
        payload.short_description = current.short_description.toString();
        payload.description = current.description.toString();
        payload.priority = current.priority.toString();
        payload.state = current.state.toString();
        payload.assigned_to = current.assigned_to.toString();
        
        // Convert the payload to JSON
        var jsonString = JSON.stringify(payload);
        
        // Set up the REST message to Tines webhook
        var restMessage = new sn_ws.RESTMessageV2();
        
        // Replace with your actual Tines webhook URL
        restMessage.setEndpoint("<https://your-tenant-name.tines.com/webhook/path/secret>");
        restMessage.setHttpMethod("POST");
        
        // Set headers
        restMessage.setRequestHeader("Content-Type", "application/json");
        
        // Optional: Add authentication if required by your Tines webhook
        // restMessage.setBasicAuth("username", "password");
        // OR
        // restMessage.setRequestHeader("Authorization", "Bearer your-token-here");
        
        // Set the request body
        restMessage.setRequestBody(jsonString);
        
        // Execute the request
        var response = restMessage.execute();
        
        // Log the response
        var responseBody = response.getBody();
        var statusCode = response.getStatusCode();
        
        gs.log("Tines webhook response status: " + statusCode, "Tines Integration");
        gs.log("Tines webhook response body: " + responseBody, "Tines Integration");
        
    } catch (ex) {
        gs.log("Error sending data to Tines webhook: " + ex.message, "Tines Integration");
    }
    
})(current, previous);
```

## Verify the connection works.

In this case, I used the Incident table in ServiceNow and to test I created a new record and after submitting, viewed the data populated in the Tines webhook.

From there, continue building out the rest of your story!

![](https://downloads.intercomcdn.com/i/o/am0g137l/2192633330/b6b6d627697d436c1aa66b3ff77e/edce3e88-83c1-426e-a512-ecd67a09ffe0?expires=1784851200&signature=2d428cea0dd414889c9eb2a99eed314fe08488ae54726d84dd3210df84f3d89a&req=diEuFM99noJcWfMW3nq%2BgWuzrD6yXa2i%2F5MVu6i8X3sDkKXH9pD0UgeIdtwr%0AGUv0eHgUGEhDeJe1VqAnT1Hwy9M%3D%0A)

## How to create a case in Tines

[Watch video](https://www.youtube.com/embed/i_Idcr_x79A?rel=0)

For more, [read our introduction to cases doc here](https://explained.tines.com/en/articles/7971194-introduction-to-cases).

## How to edit a record template in Tines

The below video demonstrates how to edit a record template in Tines.

[Watch video](https://www.youtube.com/embed/3U6uUsMffFs?rel=0)

## How to enable Tines single sign-on (SSO) via SAML with Okta

## Overview

Tines supports SSO so that your users can sign into your Tines tenant with their existing account via your external identity provider. In this article, we cover how to specifically set up non-JIT SSO between Tines and the popular identity provider, Okta.

> **Note:** SSO only enables users to sign in, not sign up. Users must have existing accounts in the Tines tenant beforehand to gain access or [just-in-time provisioning](https://www.tines.com/docs/admin/user-administration/jit/) must be enabled.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Single sign-on](https://www.tines.com/docs/admin/single-sign-on/)
    
-   [Okta - Create SAML app integrations](https://help.okta.com/en-us/content/topics/apps/apps_app_integration_wizard_saml.htm)
    

> **Tip:** We recommend performing this configuration in one browser window and testing it in a separate private window or a different browser. With this, if there's any issue in configuration, an active session to Tines will still be available to fix errors or rollback changes.

## Make it happen

### Create an Okta application for Tines

> **Tip:** Do not use the Tines app within Okta's **App Catalog**. You will need to create a custom app in Okta following the steps below.

1.  Sign into your Okta environment as an administrator.
    
2.  Navigate to the **Applications** menu → **Applications** → **Create App Integration**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384178608/59f4567f4eb11124216777d0ba00/image.png?expires=1784851200&signature=49836948cfc4e4248e0ab6d1128843fe86df08363bee43c095b2c56273f30c28&req=dSMvEsh5lYdfUfMW3nq%2Bgfdwjd5T9Fk%2B7B1cuRMoNLEom7N3O87RvMf2YjSF%0AwAS5cOkB22Wx6wdFC1JIM4b3RF0%3D%0A)
    
3.  In the **Create a new app integration** pop-up window that appears, select **SAML 2.0**, then **Next**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384182980/992217c2609f92140baddacdd189/image.png?expires=1784851200&signature=b5e7e835e6511714f85aa73287763b2e8c98f1ce68ff19ccb666a8ac8097c8c9&req=dSMvEsh2n4hXWfMW3nq%2BgQ%2FNn1f4CeyY4wtmG%2Brc1uGAtcOrYNPnzAgnItTB%0AKfm3REm1Qdxqr2enkkhNWc23WP4%3D%0A)
    
4.  In the **General Settings** step, enter "Tines" (or something else meaningful) as the **App name**. Optionally, you can also add the [Tines icon](https://explained.tines.com/en/articles/10570669-where-can-i-download-tines-logo) as the **App logo.** Then, click **Next**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384190230/fc5b7f119add562b56ef5003fad5/image.png?expires=1784851200&signature=2a38b6f1ea5908d706169d6a87904e69c4428ab7749740aa8e6c4d6a0ed751f2&req=dSMvEsh3nYNcWfMW3nq%2BgbuLf2ZK%2FpuIchN%2FxCUpkNY81NM2ZxyRqsmoT8HF%0AKtIKzhllior6%2F9deYiyFEo%2FFfQY%3D%0A)
    
5.  In the **Configure SAML** step, set the following parameters; the remaining fields can be left blank or default (see screenshot below for an example):
    
    1.  **Single sign-on URL**: `https://<your-tines-tenant-domain>/users/saml/auth` (leave the **Use this for Recipient URL and Destination URL** option checked)
        
    2.  **Audience URI (SP Entity ID)**: `https://<your-tines-tenant-domain>/users/saml/metadata`
        
    3.  **Name ID format**: `EmailAddress`
        
    4.  **Application username**: `Email`
        
        ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384216577/a36037593e8e2361d8748114b896/image.png?expires=1784851200&signature=ab87fd681965ff57a91b8c7c9551e02cd16939a448d09cdad885eda78e31d66e&req=dSMvEst%2Fm4RYXvMW3nq%2Bgad7KMq00EQNNK9Gnrg0nsydFxzlobT2PB4bL5pC%0AtHqFMdmZBRdkV3QKzQy5wM%2Bvblc%3D%0A)
        
    
6.  Optionally, you can click the **Preview the SAML Assertion** option to confirm the information you've entered is correct. Otherwise, scroll down and click **Next**.
    
7.  In the **Feedback** step, you can skip this as it is optional; click on **Finish**. This finalizes the creation of your new Tines app in Okta.
    
8.  Within the **Assignments** tab, add the **People** and/or **Groups** that you want to have access to Tines. **Note:** If you do not have [JIT enabled in Tines](https://www.tines.com/docs/admin/user-administration/jit/), make sure that these users have *an existing account in both Tines and Okta with matching email addresses*:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384230811/26f5fc2bedae8a6de912e21d75ef/image.png?expires=1784851200&signature=6ea8ac5b20907ea9117e6b83bb20bbc93b03c1dae26b7205b7ab0f1ddc19c81c&req=dSMvEst9nYleWPMW3nq%2BgRIr6MSd1aKOPsbAKU6ct5HTICAa3kPJZwQIypar%0AcoFH9xJ%2F3w790nW3wj8l6wpLAnY%3D%0A)
    
9.  Navigate to the **Sign On** tab → in the right-panel via **SAML Setup**, click **View SAML setup instructions**.
    
10.  Copy and save the following values locally; they are used in the Tines configuration:
     
     1.  **Identity Provider Single Sign-On URL**
         
     2.  **X.509 Certificate** (including the `BEGIN CERTIFICATE` and `END CERTIFICATE` liners)
         
         ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384257012/10f22eaaddb255e136a477030318/image.png?expires=1784851200&signature=3d95ac3e0a942c2f7525b2217e4e096c5ee78bf0ade998b54c541c622ec78d45&req=dSMvEst7moFeW%2FMW3nq%2BgU%2FmdNIKEKuhWg1%2FTcJ8ZYh1ySmlQ%2Bp4uAvrrte7%0A6vsYVOvirEWKH7YVbBaSmr%2BQzs8%3D%0A)
         

### Configure the authentication settings in Tines

1.  Sign into your Tines tenant as a tenant owner.
    
2.  Navigate to the tenant owner menu → **Settings** → **Access & security** → **Authentication**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384262558/9a7843864672ad9f90d67c2c006b/image.png?expires=1784851200&signature=78378b8d3f49fde1889014095beb54ad4a1af306ef0f895d0e5005f07bea7e13&req=dSMvEst4n4RaUfMW3nq%2BgVxjs3Fsf74l8rHq%2BeYq5h84tsKBhUDJYmKKPgd0%0AM4FBdkhTr%2BSupJOLherESnblzqM%3D%0A)
    
3.  In the **Authentication settings** pop-up window, set the following parameters:
    
    1.  **Authentication type**: `Single sign on (SSO) via SAML`
        
    2.  **SAML identity provider URL**: Your Okta app's **Identity Provider Single Sign-On URL**
        
    3.  **SAML identity provider public certificate**: Your Okta app's **X.509 Certificate** (including the `BEGIN CERTIFICATE` and `END CERTIFICATE` liners)
        
        ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384266385/c3b9f7204940a30cafee9706d31d/image.png?expires=1784851200&signature=f282d962817655694559749f315c91c92d1d3fa740c691190adc579ab83cea54&req=dSMvEst4m4JXXPMW3nq%2BgWbG%2BjjmqO1c94qg2iUJLihm1nQ7trP7WgLCJ%2Bjv%0An9%2ByzSDe8oPsEts2RhKQSOTvOH4%3D%0A)
        
    
4.  Review that the information is correct, then click **Save**.
    

## Review the results

Now, when your end-users access their Okta dashboards, they can click on the Tines app, utilizing SSO to log into your Tines tenant:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1384286410/d56ad4501115adb94d627baf55eb/image.png?expires=1784851200&signature=5e1bdd13c9786a123498c293f49441c69d994d7d669ad872dea7d834474ed044&req=dSMvEst2m4VeWfMW3nq%2Bgd9gtyV0Svb51BViUCHyskEMXuip8nu3lgxQ0I%2Fn%0ALTB2zq83wlKPF8WJmzrZmMaWOEU%3D%0A)

## How to enable webhook API responses in Tines

## Overview

This article covers how to enable webhook API responses in Tines for use cases like sending a custom response to an incoming API call.

## Tines manual reference

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   Tines: [Webhook Action](https://www.tines.com/docs/actions/types/webhook)
    
-   Tines: [Event Transformation Action](https://www.tines.com/docs/actions/types/event-transformation)
    
-   Tines: [HTTP Request Action](https://www.tines.com/docs/actions/types/http-request)
    
-   Tines: [Workflows as APIs](https://www.tines.com/docs/stories/apis)
    

## Make it happen

Within your storyboard:

1.  Drag-and-drop the following actions:
    
    1.  Webhook
        
    2.  HTTP Request
        
    3.  Event Transform  
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/1003114471/df6f54dd3c8cc0d61326661c/image.png?expires=1784851200&signature=2dad70516f58f0a5b4048a3491d88eeb93c0dfea2c3ef3c21d2de7b9c75d126b&req=dSAnFch%2FmYVYWPMW3nq%2BgZo9ELbtEnfxu6FonP08aoQdmfwEomLSyHPSeHzi%0AHh7A49FjtEopn5dbHNdUxXTLNWU%3D%0A)
        
2.  Navigate to your story configuration panel. You can do this by clicking in an empty space within your storyboard.
    
3.  Within the story configuration panel, toggle on the "Enable webhook API responses" setting:  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/1003105509/ae2423b0272ba56951bc02ba/image.png?expires=1784851200&signature=326bfbbc556e63e08152c771924412dbea49f333c6013ba60462e36c7b300af5&req=dSAnFch%2BmIRfUPMW3nq%2BgQC8ungcTv9%2BgWR%2BAgLMtyO3RKZ6nArFBycnG9Wj%0ACZvzidKRBuiFNHXtt8i%2FMqsr3Mo%3D%0A)
    
4.  In the pop-up that appears, select your Entry action(s) and Exit action(s).  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/1003116577/23b7ea6ff366b4af3011ff09/image.png?expires=1784851200&signature=52565bbc5cf6d938cd317e0a90403c8406639404e9ffed1b21a73301010518ac&req=dSAnFch%2Fm4RYXvMW3nq%2BgSIcw%2BtNG8Aqyn5Mip5%2Bv2DMMyM57ruIC96xDKSm%0AvSIgaP1BCoPrUkJ6ZOmMln%2Bukro%3D%0A)
    

## Review the results

Looking at your storyboard, you will now see arrows on the relative Entry and Exit actions:

![](https://downloads.intercomcdn.com/i/o/1003124567/6b268c6381effb98a30c1197/image.png?expires=1784851200&signature=503dcd3fe2eac0f2c6833db8834e92dd4a464a814dedfdfa705bdb0eab84e52f&req=dSAnFch8mYRZXvMW3nq%2BgXIWg2iLhNKKWjU8isky5bDUvqP1FpIz1VNPVTSL%0A3QL68l%2Bn5fdRKGKCV9lRSf4W4YE%3D%0A)

Configure your HTTP request URL to point to the Webhook URL and run it to see the event output:

![](https://downloads.intercomcdn.com/i/o/1003128399/395893de5000f118accd0ecb/image.png?expires=1784851200&signature=ccd75c6fe88c252f085f236efdab643ea1d3679d680b03a91a1d24a63f666dbc&req=dSAnFch8lYJWUPMW3nq%2BgdWWverdNhkKjs3y9vRH4BgSf7vQSDcDoD7zjZeP%0AB6%2FXZktdlTQgMYDyAUo5ef0A7Lo%3D%0A)

## How to find commonly used assets

#### Tines' logo and other branding essentials

As an example, when creating an app in your 3rd party platform to connect to Tines, attaching our logo helps to visually identify Tines in your toolkit. You can access our brand assets by following these steps:

1.  Navigate to the [Tines website](https://www.tines.com/).
    
2.  Right-click on the Tines logo in the top-left corner.
    
3.  Select **Access all Tines brand assets**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384083691/be9e1d1c3da4569aa145e803bc85/image.png?expires=1784851200&signature=00cb23a03a53c8f2fc78a5145aa183c891a9f85c8e14403995b28321aef8f7df&req=dSMvEsl2nodWWPMW3nq%2BgXXxAAlTrOAetU4BTUX87kafaaHQ0QN3RyvBGEuE%0ACZg6oNZheEQxSu2U65pvidiVbTs%3D%0A)
    
4.  Click **Download brand essentials**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384086668/9c5a828ad5d368c12c33b2637ffa/image.png?expires=1784851200&signature=0b70f70aad0117f3af15e26ea856861fa555817a6696c01546f88a13fb35161b&req=dSMvEsl2m4dZUfMW3nq%2BgUapisaqAugMVQ5AQtp1LMNtLWLePSogT6j0YStv%0Aqb6yfxc4qwr%2BcFxIdW2aW4fMfIU%3D%0A)
    

This will locally download a ZIP file named **Tines Brand Essentials** that contains AI, PNG, and SVG files of our full logo and icon.

#### **Tines' customer Data Processing Agreement (DPA)**

Tines' Data Processing Agreement (DPA) can be found on our website here: [Customer data processing agreement](https://www.tines.com/dpa).

## How to leverage templates and stories in Workbench

In this video, we'll take a look at how we can activate available templates and stories to our Workbench instance.

[Watch video](https://www.youtube.com/embed/9txpybCFWNo?rel=0)

## How to leverage the AI Agent action in Chat mode

[Watch video](https://www.youtube.com/embed/MKjuB2LSCG0?rel=0)

### Video transcript

Hi, Angie here from Tines. In this video, we'll focus on the chat mode of our AI agent action.

Chat mode creates a dedicated URL where end users can have back and forth conversations with your agent. Unlike task mode, which runs autonomously, chat mode is designed for interactive experiences where human input guides the conversation.

To configure an AI agent action in chat mode, add a new AI agent action to your story. Then select chat mode from the mode options.

In the configuration panel, you'll see the same system instructions field as in task mode. While the system instructions work the same way, you'll want to tailor your instructions for interactive conversations when using chat mode, such as define the agents persona with a conversational tone, include guidance on how the agent should respond to different types of questions, set boundaries for what topics the agent should or should not discuss, as well as consider including example dialogues to guide the agents conversation style.

Just like in task mode, you can add tools, custom tools, and send a story for your chat interface. To do so, navigate to the tools section in the configuration panel and add tool. Here you can add relevant tools including send to story and custom tools that end users might need during conversations.

The agent will suggest using these tools when appropriate during the chat. Once configured, you can share your agent chat interface via copying the generated URL manually from the copy link option in the toolbar. Or if you need to reference the URL in a connected upstream action, utilize our page function to share the URL more autonomously.

Depending on your access control settings of your AI agent action, end users can access the chat interface directly through their browser via this URL. You can review chat interactions to improve your AI agent actions performance. In this example, we'll take a look at an AI agent action that is set up to chat with users on how they can improve the write-ups of their existing Jira issues. Once the chat is complete, you can view the conversation history in the AI agent actions events panel.

Here, analyze which tools were used during the conversations and use these insights to refine your system instructions and available tools.

Now, some general best practices. Keep system instructions clear and conversational for chat mode.

Only add tools that are relevant to the types of conversations end users will have.

Consider the end user experience when designing your agents persona and responses.

Regularly review conversations to improve your agents effectiveness.

Finally, a few key takeaways from today's video. Chat mode enables interactive conversations through a dedicated URL. System instructions should be tailored for conversational interactions. Tools can enhance the chat experience by allowing the agent to perform actions. Monitoring conversations helps improve your agents performance over time. See you in the next video and happy building.

## How to leverage the AI Agent action in Task mode

In today’s fast-paced environment, manual processes can be a bottleneck. The AI agent action in Tines is designed to break those barriers by integrating intelligent decision-making directly into your automation workflows.

Read [this article](https://explained.tines.com/en/articles/11541238-get-started-with-agents) to help you get started with AI agents in Tines.

While the AI agent offers two modes, chat and task, this guide focuses on task mode, which enables the agent to independently make decisions and execute actions without human intervention. This is the ideal solution for fully autonomous workflows.

[Watch video](https://www.youtube.com/embed/o0vYUqSfrqA?rel=0)

### Getting started with task mode

Configuring an AI agent action for task mode is straightforward. Follow these steps to get your agent up and running:

1.  **Add the action:** Navigate to your Tines story. Select the AI agent action from the story toolbar and drag it onto your canvas.
    
2.  **Select task mode:** With the action selected, open the properties panel on the right. Under the mode settings, choose task mode.
    
3.  **Configure instructions:** Once in task mode, you can begin defining your agent’s behavior through system instructions.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2352468452/c36f8f9dd14bacd4b36830d4064e/task+mode+ai+agent+st.gif?expires=1784786400&signature=e7af9a7e73cff3caa4a6a364f3f157ad61547f66f92d342b34d4efa32f8bca75&req=diMiFM14lYVaW%2FMW3nq%2Bga5Cx%2FOjvkbpGsGc1xF%2FzUf8x36g6bKcX4hApDtQ%0ARkQbjpZLuE6U8UmdfsaOEy1%2FXvI%3D%0A)

### Crafting effective system instructions

Your system instructions are the "brain" of your agent. They define the tone, domain, and specific persona the agent will adopt. A well-defined prompt leads to more precise and reliable outcomes.

### Best practices for your prompts

-   **Define a persona:** Give your agent a clear identity. For example, if you are creating a cybersecurity assistant, define the persona as a cybersecurity analyst responsible for reviewing alerts, and provide context like: "You work on a security team tasked with investigating potential threats."
    
-   **Stay organized:** As your system instructions grow in complexity, use clear, distinct sections. This improves maintenance and helps the AI understand its role and boundaries more effectively.
    

> This [article](https://explained.tines.com/en/articles/11644147-best-practices-for-the-ai-agent-action) goes through more best practices for the AI agent.

### Integrating dynamic data

The power of the AI agent lies in its ability to process real-time information. You can inject dynamic data into your prompts by using data pills.

-   **How to add data:** Within the prompt input field, simply click the + button.
    
-   **Referencing values:** This allows you to select values from upstream actions or resources. Once selected, you can develop your JSON path to ensure the agent receives the exact data it needs to perform its task.
    

**Example:** You might prompt the agent to "summarize the data," followed by a data pill referencing upstream data. This ensures the AI always has the most current information to work with.

## How to manage folder structure in Tines

This article explains how to leverage folder structure and hierarchy to better organize assets within your tines tenant. Even if you are limited to a single team, you have the flexibility to create an effective organizational system.

## Creating folders

You can create folders within the stories, credentials, and resources sections to categorize your assets.

### Stories

-   Navigate to the section you wish to organize, for example, stories.
    
-   Click the dropdown beside the +New button in the top right corner of the screen.
    
-   Select Create new folder.
    
-   Give your folder a name and click Done.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2272747936/01ca5ab666c96b9a46c16a736cc3/create+folder+for+stories.gif?expires=1784851200&signature=c3d9beb02165c0503020a4eb9744b789d895cce31ba455bed455278029198023&req=diIgFM56mohcX%2FMW3nq%2BgX3QwzBa8ZPKFOOIhBjDbj1NMVmdXmZ6zWG6%2Byrt%0AOtnTZjPCPoq1YBMH0pc%2F6Ybzymg%3D%0A)

### Credentials and resources

-   Navigate to the credentials or resources screen.
    
-   Click the folder icon beside the +New button.
    
-   Give your folder a name and click Done.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2272749037/9b96966f3d62431215dd9a751873/create+credential+or+resource+folder.gif?expires=1784851200&signature=796b3fe5f3df4fdeb99ced531012c77d184f03ab33dd80f150e55b0b791282da&req=diIgFM56lIFcXvMW3nq%2BgVWQqcaSm57CQ97u8DMY03ocsqOavSggfIEY5HwQ%0A1J6K5c1pq5SWXBWDmlYSDwwQkXg%3D%0A)

## Moving a story into a folder

-   Locate the story you want to move.
    
-   Click the three dots next to the story name.
    
-   Select Move.
    
-   Choose the specific folder you want the story to go into and click Move.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2272749712/ae986f1cb332a7e699db6d8eadc9/move+story+to+folder.gif?expires=1784851200&signature=699a12892d7435e7fb5e627be3e40950ac373a855fd99ab9833712dbde871525&req=diIgFM56lIZeW%2FMW3nq%2BgQlzE6PcvG1k2Ie5L6eLbs31Lwc0X6nJW6aS1X6m%0AMnURaBPtw5T1V8QcAxU2TKhe714%3D%0A)

## Example folder structures

### Stories

-   Create folders like "testing" and "in production" to separate development and live stories.
    

### Resources

-   Create folders like "URLs" to store and reference common urls used when building stories.

## How to pitch AI in Tines to your GRC team

Bringing an LLM into your environment, especially one that interacts with your most important workflows, is a conversation that should not be taken lightly. Whether your company is looking to use Tines default AI or your preferred AI provider, it will most likely need to be approved by internal teams.

We’ve created this guide to help you pitch using AI features in Tines to your GRC teams. Follow along to get set up for success and know what questions to expect.

### **Getting started**

Before you kick off the conversation, here are some steps you should ask yourself:

-   **Who within my organization should I approach to pitch AI in Tines and get approval?**
    
    -   It depends on the organization, but typically customers will go to their IT, Security, Legal, GRC, and/or Finance teams.
        

-   **What information should I have on hand to start the conversation?**
    
    -   What LLMs/services are available
        
    -   What type of data will be processed
        
    -   Use cases planned for AI in Tines
        

If your company has an existing LLM in place that is compatible with Tines ([see available AI providers here](https://explained.tines.com/en/articles/10371885-use-a-custom-ai-provider-in-tines)), this can be an advantage. However, we also have a great security implementation for our in-product AI.

### **Tines resources to have on hand**

Take a look through the available Tines resources on AI. They include various best practices and our policy on maintaining secure and private AI features.

-   Blog: [How to evaluate AI features in workflow automation platforms](https://www.tines.com/blog/evaluating-ai-features-workflow-automation/)
    
-   Blog: [Implementing AI within your security strategy: 7 best practices](https://www.tines.com/blog/implementing-ai-within-your-security-strategy-best-practices/)
    
-   Blog: [AI in Tines: secure and private by design](https://www.tines.com/blog/ai-in-tines-secure-private-by-design/)
    
-   Blog: [Securely bringing your own AI to Tines](https://tines.com/blog/securely-bringing-your-own-ai-to-tines/)
    

-   Explained: [Security & privacy for AI in Tines](https://explained.tines.com/en/articles/9369078-security-privacy-for-ai-in-tines)
    
-   Explained: [Use a preferred AI provider in Tines](https://explained.tines.com/en/articles/10371885-use-a-custom-ai-provider-in-tines)
    
-   Explained: [AI in Tines articles](https://explained.tines.com/en/collections/9473753-ai-in-tines)
    

-   FAQ: [Tines Trust Center | Topics and common questions](https://trust.tines.com/)
    
    -   See questions on how Tines’ AI features are powered and how data is handled
        
-   FAQ: [Preferred AI providers](https://explained.tines.com/en/articles/10389743-frequently-asked-questions-custom-ai-providers)
    

-   Docs: [Admin-AI](https://tines.com/docs/admin/ai/)
    
-   Docs: [AI Agent action](https://www.tines.com/docs/actions/types/ai-action/)
    
-   Docs: [Automatic mode in Event Transform](https://www.tines.com/docs/actions/types/event-transformation/automatic/)
    
-   Docs: [Workbench](https://www.tines.com/docs/workbench/)
    

### **Questions to expect**

Now that you’ve gotten an audience, what types of questions can you expect them to ask? We asked our own Legal, GRC, and IT/Security teams, and these were their answers:

-   **What top question(s) will the Legal team ask?**
    
    -   How will the AI model be used with our workflows?
        
    -   How is our data protected?
        
    -   Is our data used to train any AI models?
        
    -   What policies will be shared with the rest of the organization?
        

-   **What top question(s) will the Security team ask?**
    
    -   What data is involved?
        
    -   Where will it be stored/processed?
        
    -   How long will it be stored?
        

If you opt to use the AI already available in Tines, you can find answers to these questions in the [Tines Trust Center | Topics and common questions](https://trust.tines.com/). If you choose to use a different model, you can find those answers with that vendor.

As always, the Tines team is here to help you navigate this discussion. If you have any questions, please don’t hesitate to reach out.

## How to set up an MCP server in Tines

## Overview

**[Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro)** is an AI standard [introduced by Anthropic](https://www.anthropic.com/news/model-context-protocol) that defines how large language models (LLMs) can interface with external systems in a structured, predictable way. MCP enables LLMs to invoke remote procedures, query external data sources, and perform context-aware operations through a unified protocol. In Tines, you can build and deploy custom [MCP servers](https://www.tines.com/docs/actions/templates/mcp-server/) (via a templated action) directly from your storyboard, allowing your LLMs to interact with your integrations in real time.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   **MCP**: [Example Clients](https://modelcontextprotocol.io/clients)
    
-   **Tines Blog**: [Building MCP servers on Tines](https://www.tines.com/blog/introducing-mcp-servers/)
    
-   **Tines Explained**: [MCP server FAQs](https://explained.tines.com/en/articles/11932675-mcp-server-faqs)
    

> **Tip**: Need some inspiration? Check out [this story](https://www.tines.com/library/stories/1324549/) in our Library for MCP server ideas!

## Make it happen

### Video Guide

[Watch video](https://www.youtube.com/embed/Rh3KUvW6Mew?rel=0)

### Step One: Verify your MCP client supports MCP integrations

Make sure your MCP client meets our [MCP-supported features](https://www.tines.com/docs/actions/templates/mcp-server/#protocol-details) and that you understand the [limitations](https://www.tines.com/docs/actions/templates/mcp-server/#limits).

> **Note:** Our MCP server does not consume AI run-time credits.

### Step Two: Configure the MCP server in Tines

Within your Tines storyboard:

1.  Navigate to **Templates** in the left-hand panel.
    
2.  Search for "**MCP**":
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1653322568/0944813a7590f997230ce438b068/image.png?expires=1784851200&signature=e86e71f1a2285e8ee99b92363ef5fb1226cdb7d863fef0433fd407fe317b15aa&req=dSYiFcp8n4RZUfMW3nq%2BgWjmYpIz1J0xQnd5K5jouP7LR%2BDHUb8w8Cp6fDPW%0Afs4TtWyk19hrSbfmMwODGtV0tss%3D%0A)
    
3.  Drag and drop the template onto your storyboard.
    
4.  Select "**MCP Server**."
    
5.  Configure the action as needed, including adding **Tools** (similar to our [AI agent action](https://www.tines.com/docs/actions/types/ai-action/#tools)).
    
    1.  Ensure your connected Tools are properly authenticated with [Credentials](https://www.tines.com/docs/credentials/).
        
    2.  Make sure to check the **\+ Option** button to add additional settings, such as **[Access Control](https://www.tines.com/docs/actions/templates/mcp-server/#access-control)**.
        
6.  Within the **Summary** tab of the action, note the configuration options relevant to your MCP client's environment (**Remote Server** or **Claude Desktop**).
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1653372386/4974e992132a5d44ee7206602c34/image.png?expires=1784851200&signature=45bc653c908d7b7d82e48b221accdeca574948addc612ba4219c448d31de57a0&req=dSYiFcp5n4JXX%2FMW3nq%2BgW8k8qltPbjBNN%2FNusz4QcwMdVYg70x%2BQQYV0IP%2F%0AAc25z2tvKnAlJpGeWeDBmpjfKL4%3D%0A)
    

### Step Three: Link the MCP server via your MCP client's connectors

Depending on your MCP client, apply the **Remote Server** or **Claude Desktop** configuration in the MCP client's settings. The pre-work and location of this setup will also depend on your MCP client, so we recommend researching your MCP client's documentation for how to configure a connector.

## How to set up reporting in Tines

In Tines, tracking your progress is more manageable with Reporting.

You'll see tenant-level metrics like

-   Most activity by:
    
    -   Story
        
    -   Action
        
    -   Credential
        
    -   Resource
        
-   AI credit usage
    
-   and more!
    

Tenant-level reporting is available via you main menu.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1516016411/485adeebbe9f179b53c0e76b84d6/CleanShot+2025-05-09+at+13_22_03%402x.png?expires=1784851200&signature=f22742b00e4d3cb7cdd6ffbd74283f60ac70543d8342a2e608cb52d8aea210eb&req=dSUmEMl%2Fm4VeWPMW3nq%2BgZRlarahtMB%2FYyDKXOwIDe1DKyp2URDhf8FQ97bV%0AmtLQVNvZkLRsRS64GDrifiElaF8%3D%0A)

You can set action-level reporting, too, to see time-saved. You can configure it in the action status tab of the Storyboard.

Time-saved reports do not work on draft Stories. Your Story must be published first.

## Add reporting to an action

1.  Open the Storyboard and choose the action you want to monitor
    
2.  Choose the status tab in the properties pane
    
3.  Click the + sign next to reporting to add your benchmark
    
4.  Choose the unit (minutes or seconds) and input the value (i.e., 1, 2, 30)
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1516022147/9b3b5cee78afbfb3e9e8a35ea0b3/CleanShot+2025-05-09+at+13_27_50.gif?expires=1784851200&signature=75e5cc49d07ac4290b8d37ec558970e5c0bdc1a4bcc8bcdbf9f167f9421aafcf&req=dSUmEMl8n4BbXvMW3nq%2BgTC3%2BZdHXfisdKVL99U%2Bs%2BmTGiJY3aMVpCj0fJQw%0AuHjGtAkHvgBOosWBruZoYs%2FpXNI%3D%0A)

And that's it! As your actions with reporting enabled run, the accumulated data will display on the reporting overview chart (available via the main menu in the top left).

## How to set up Zoom webhooks to Tines

## Overview

While building throughout Tines, you may need to send event-based notifications from Zoom into your story. This article goes over how to set this connection up using a Tines webhook action.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Webhook action](https://www.tines.com/docs/actions/types/webhook/)
    
-   [Zoom - Webhooks](https://developers.zoom.us/docs/api/rest/webhook-reference/)
    

> **Tines Tips:**
>
> -   Zoom uses a **challenge-response check (CRC)** for webhook validation. When a CRC occurs, Zoom makes a POST request to your endpoint with a challenge request body. After your endpoint receives the request, your app needs to respond with the challenge response within 3 seconds.
>     
> -   Zoom requires that you manually trigger webhook validation when you add a new webhook or make changes to an existing one. Subsequently, Zoom automatically re-validates webhooks every 72 hours.

## Make it happen

### Step One: Zoom app configuration

1.  Within Zoom, navigate to the [Zoom Marketplace](https://marketplace.zoom.us/) → **Develop** → **Build App**.
    
2.  Zoom's API License and Terms of Use pop-up may appear. Read through this and click on **Agree** to continue.
    
3.  For "What kind of app are you creating", select **Webhook Only App**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223336299/c71d32660943af4800825efb0072/image.png?expires=1784851200&signature=90b77100a2eb58e55c33b1b731452a6badf5b01ca2303a09e7e1ddf2ba191275&req=dSIlFcp9m4NWUPMW3nq%2BgTmW96Q7Ky8Mp%2BGCOnSL54Z72kVm8F%2Bu%2Feiiy%2BvQ%0ACpWs0%2BITwQE4hRawdhLfVC%2FsRa4%3D%0A)
    
4.  Click **Create**.
    
5.  Name your app via **App Name**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223338525/f29beea96265787cbfdfbf04e936/image.png?expires=1784851200&signature=ff53c227df654fdfbe6e1fda3f2b8ecd37167679fd747edf271294b9037f1358&req=dSIlFcp9lYRdXPMW3nq%2BgW5m832XcWXb8OrbYBjtpIPMgYtZFSKINzD48iu%2F%0AbrPCjP7uisMXk0hqauJOk6vIllE%3D%0A)
    
6.  Click **Create** again. This will bring you to the **Information** page of your app.
    
7.  Make sure to fill out the rest of your details in the **Information** page (you won't be able to activate the app without this).
    
8.  Navigate to **Feature** and copy your **Secret Token** value. You'll be using this later on.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223335118/eb6e20f926df121bf84fb73e074f/image.png?expires=1784851200&signature=2f1156accfd2f26a185ccc7b72cf680c93661044c210ec53192610f76bf126e3&req=dSIlFcp9mIBeUfMW3nq%2BgaF6a7%2FW0R2iS1%2BRnuCHqalkkt%2FBddwQi%2B9P8uwA%0AX%2FTtm1PXNrln5%2BHdBsWMDRV%2Bjx8%3D%0A)
    
9.  Still on the **Feature** page, toggle on **Event Subscriptions**.
    
10.  Click **\+ Add Event Subscription**.
     
     ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223341573/87b22fc51320d94699a6f173f646/image.png?expires=1784851200&signature=b55cd7125df5ad44cb465cfccf214e007b5494817770ea64e5485e6abb5c4fa0&req=dSIlFcp6nIRYWvMW3nq%2Bge0KEf%2Fte2zHqebw3C8vM1n8DnNtxScUlu9EwkPC%0As1F9iAxx0waC%2Bv7hb3%2BPOw07578%3D%0A)
     

### Step Two: Create your Zoom secret token credential in Tines

1.  In a new browser tab, navigate to your Tines tenant -> team → **Credentials**.
    
2.  Click **\+ New** → **Text** to create a text credential.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223353703/2a4b4d6e664a727fdb01a4131049/image.png?expires=1784851200&signature=63af4470083923da805e1e2c0057dec97e818e5e0e5762a4e84943b51a926448&req=dSIlFcp7noZfWvMW3nq%2BgfKvNzIlPU1GDdTRkKc8dvcGtyojIqF%2FhrIXtv6t%0AYXDuMbJn2zye2HdGkrZisP2au0U%3D%0A)
    
3.  Name the credential "Zoom Secret Token".
    
4.  Paste your Zoom secret token you copied from earlier into the **Value** of your credential.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223360549/2649168d9553bea881ae980a99c6/image.png?expires=1784851200&signature=438ea96d76cde9b121f61187bc580b82cee99baf5e921ae2d6ead63158373275&req=dSIlFcp4nYRbUPMW3nq%2BgY4lZIeKLJXh1bp6LTrOhC4aOCZQiK8JJh4KIVnn%0ABYj914iE86MqVGuQO9jVl9%2F4tYo%3D%0A)
    
5.  Click **Save** to save your credential.
    

### Step Three: Grab your Tines webhook

1.  In your Tines storyboard, drag in a webhook action.
    
2.  Rename the webhook action: "Zoom Receive Events".
    
3.  Copy the **Webhook URL** value.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223369853/f9db918c9d381f01c961089b0956/image.png?expires=1784851200&signature=f1672a9f4b183f7af90988f4ce8085a667e96bc42d3fb7b36e9e1c087d6f584e&req=dSIlFcp4lIlaWvMW3nq%2BgRpCnULMeasvJlBR%2BV%2BtjR3tZPvWZ0AdVbbKPqoD%0A5%2Bvgn3m8Ur%2BlMxyVLcuRnqWlFfo%3D%0A)
    
4.  Back in your Zoom app -> event subscription you created earlier, paste the Webhook URL into the **Event notification endpoint URL**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223375635/167fe0a3243493eddc09fd2fd1f0/image.png?expires=1784851200&signature=05e2d0b55c05bc950f869fe92ae66c8067a702e87192e370dc204057198c5fa6&req=dSIlFcp5mIdcXPMW3nq%2BgdTgagVRXfTbM5ZhfAdnw8kIiEGALASbc3QCU%2FlN%0ASy61dBzoHtUOKL2pUoosYeBmcSg%3D%0A)
    

### Step Four: Validation setup

1.  Back in your Tines storyboard, drag in an event transform action.
    
2.  Name the event transform action "Challenge Response".
    
3.  Connect the event transform action *after* your webhook action.
    
4.  Set up the **Payload Builder** of the event transform action like so (you can copy +paste the following into the "Plain Code" of your payload):
    
    ```
    {
      "plainToken": "<<zoom_receive_events.body.payload.plainToken>>",
      "encryptedToken": "<<HMAC_SHA256(zoom_receive_events.body.payload.plainToken,CREDENTIAL.zoom_secret_token)>>"
    }
    ```
    
5.  In your story settings, turn on "**Enable webhook API responses**" with the following setup:
    
    1.  **Entry action**s: Zoom Receive Events action
        
    2.  **Exit actions**: Challenge Response action
        
        ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223391849/67203dd72ed629da20c648200146/image.png?expires=1784851200&signature=cdc7f45fd69fd4260bf0f2c35216a1d97af63a012219fc9fd25c6c434140abd0&req=dSIlFcp3nIlbUPMW3nq%2BgStQ7oJbd%2FYubvk80tTAOcNtWM1zMYDIAQzT7hRm%0ABB8ztXhlxMsSLgjyRJMbJ4k%2BKDQ%3D%0A)
        
6.  Back in your Zoom app, click **Validate** under **Validate the URL**.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1223403335/8e14a8accababe64441690a8183e/image.png?expires=1784851200&signature=6233d94dbffe1603db76c0ee90f592ac3d118dad5ba456c35d22cdd1f58c1cbc&req=dSIlFc1%2BnoJcXPMW3nq%2Bgdf9UtaMxYBDXdbcsHbSNqvZm4G8mx6XCdzkJ0qn%0AC18lTHCuJAuWoubeBJvIQVEXqY4%3D%0A)
    

## Review the results

After running the validation from Zoom, you will see an `endpoint.url_validation` event come through your webhook action in Tines:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1223411040/dad667d2396c985e53d3fb292da1/image.png?expires=1784851200&signature=707c21438ffed8cbae6c763fd62491d5fccf0d4ce2d716c04974b997140b09b0&req=dSIlFc1%2FnIFbWfMW3nq%2BgU74Q4wrWwGf4Nf7qfYlMCOOb1L8B0Bq3oT3TnzR%0Awn4DnBGOIE7y8vLNaWnjERdOgDY%3D%0A)

A successful "Validated" message will appear on your Zoom app:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1223405080/89b38b2f44fdb1b7709951af3aa1/image.png?expires=1784851200&signature=d257add9843866e79cac0f5011cf70062cca5f4ec7fe19c330c88b665dcfa99f&req=dSIlFc1%2BmIFXWfMW3nq%2BgVcNG9NZE9U47EzWbdW10n8G2wYaHR9PT1BwSFQo%0A2Qj%2BULFixnPtFegdzbKjBR9BwzE%3D%0A)

> **Note:** Since Zoom re-validates webhooks every 72 hours, make sure to keep your "Challenge Response" event transform action.

Once you've successfully validated the URL, you can add the Zoom events you want to pull into your story via your Zoom app's **Add Events** and **Save** your changes:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1223415277/bd3779044bd67ad4d79bd89c5b27/image.png?expires=1784851200&signature=a165df461b2ffedf833cafb304484cb31e2602343f55bc32e090862db8d0ebd1&req=dSIlFc1%2FmINYXvMW3nq%2BgSiDRdbHadUinje%2BGyXuejG7Ux259Ukzx6keuXJ1%0AJ2NsAuOEI5Du3mJ4Fq6ynM5EkXw%3D%0A)

## How to use cURL to Tines

Many services provide example cURL commands in order to interact with their API. Tines can utilize these cURL commands to create new actions on the storyboard. Instead of manually copying each field into a Tines HTTP request action, you can simply paste the entire command directly onto your storyboard.

This article shows you how to turn any standard `curl` command into a fully functional Tines action in seconds.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    
-   [Tines - Shortcuts: Import from cURL](https://www.tines.com/docs/actions/shortcuts/#import-from-curl)
    

## How it works

[Watch video](https://www.youtube.com/embed/AvyDCFWuG-I?rel=0)

1.  **Find your API example**: Navigate to the documentation for the tool you want to integrate. Locate a `curl` example for the specific endpoint you need.
    
2.  **Copy the command:** Highlight the entire `curl` string and copy it to your clipboard.
    
3.  **Paste into Tines**: Open your Tines Story and simply use the keyboard shortcut (Cmd + V on Mac or Ctrl + V on Windows).
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302852365/3f1475a031da6145af0c66abaa80/paste+curl.gif?expires=1784786400&signature=29545abb492db7d0804b8a69454e0cf353196d70064a41d541a0d279a66dc52b&req=diMnFMF7n4JZXPMW3nq%2BgS8mlhrcW0z9jvh9NLll5HLXdfr4mQj9pAgIympQ%0AViziapdLkjNk9PxS9tzvUV8vemw%3D%0A)

When you paste a curl command, Tines instantly generates an HTTP request action.

> **Note:** While the structure is created for you, remember to replace any placeholder API keys or secrets from the documentation example with your actual **Tines credentials**.

Now that your request is ready, you can start making modifications, adding credentials, and connecting it to the rest of your automated workflow!

## Why to use cURL

-   **Speed**: Go from documentation to a working action in under five seconds.
    
-   **Accuracy**: Eliminate typos in long URLs or complex header keys.
    
-   **Ease of use**: Perfect for quickly prototyping new integrations without getting bogged down in manual setup.
    

> **👉 For a more detailed description of cURL** [see here.](https://www.tines.com/docs/actions/shortcuts#import-from-curl)

## How to use webhooks

Webhooks are a way for external systems and applications to send data directly into your Tines workflows. This article covers how to create, test, and access data from a webhook in Tines.

Read the [Introduction to Webhooks](https://explained.tines.com/en/articles/14007076-introduction-to-webhooks?q=formula) article for more information.

## Creating your first webhook

### Step 1: Add a webhook action

In your Tines story, add a new webhook action. This action will be the entry point for external data.

### Step 2: Configure the webhook

Key configuration options include:

-   HTTP Methods: Choose which HTTP verbs to accept (GET, POST, PUT, DELETE, etc.)
    
-   Authentication: Optionally require secrets, tokens, or basic auth to secure your webhook
    
-   CORS Settings: Configure cross-origin resource sharing if needed for browser-based requests
    
-   Rate Limiting: Protect your webhook from excessive requests
    
-   Response Configuration: Customize what the webhook returns to the caller
    

Read our best practice on configuring your webhook names and paths [here](https://explained.tines.com/en/articles/14048386-webhook-best-practice-configuring-names-and-paths).

### Step 3: Get your webhook URL

Once created, Tines generates a unique URL for your webhook. You'll find this in the action details. The URL looks like:

<<[https://your-tenant.tines.com/webhook/abc123def456/your\_webhook\_name>](https://your-tenant.tines.com/webhook/abc123def456/your_webhook_name\>)\>

> Important: Never guess or manually construct webhook URLs—always copy the exact URL from the action details.

### Step 4: Share the URL

Provide this URL to the external system that needs to send data to Tines. Configure that system to send HTTP requests to your webhook URL when events occur.

## Testing your webhook

The best way to test a webhook is to simulate an external call from within Tines:

1.  Create an HTTP Request action in the same story (or a test story)
    
2.  Set the URL to your webhook's URL (copied from the webhook action details)
    
3.  Configure the payload to match what you expect from the real external system
    
4.  Run the HTTP Request action to simulate the external call
    
5.  Inspect the webhook's events to verify it received and processed the data correctly
    
6.  Trace downstream actions to ensure your workflow behaves as expected
    

This approach keeps your testing entirely within Tines and gives you full control over test data.

## Additional webhook configurations and uses

### Using multiple webhooks in one published story

You can utilize multiple webhooks within a single published story to facilitate your workflows. You can have two webhooks leading into the beginning of a story if your use case requires it. When doing so, ensure that the data format and the tools involved remain consistent across these inputs.

> Note that a send to story action's entry point must be a webhook action itself and is restricted to only one entry point.

### Copying a webhook action

To copy a webhook action, ensure that the secret and path are identical to the original version, and then delete the old webhook action.

You can also recreate a deleted webhook by setting up a new webhook with the original secret and path.

### Adding CORS to a webhook action

Add CORS (Cross Origin Resource Sharing) to a webhook action by selecting ‘+ Option’ in the properties panel and choosing "Enable CORS".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302427354/10a9fa0e9bab0e901c6a1c02de10/CleanShot%2B2026-03-11%2Bat%2B11_22_47.gif?expires=1784786400&signature=3b1e9a18fe92432a244c6a0facc2acf76772b1c9067dad0b14e749d32886b41c&req=diMnFM18moJaXfMW3nq%2BgZzFjIx3Qx33vynKDX0BCDIlE%2BoQK5rYALrczj8o%0AQ3mwxCFivhT6Dp1lUFNA%2FcvqcCg%3D%0A)

## Accessing webhook data

When a webhook receives a request, the data is available in the event it creates:

-   Body: `webhook_action.body` contains the request payload (JSON, form data, etc.)
    
-   Headers: `webhook_action.headers` contains HTTP headers
    
-   Query Parameters: `webhook_action.query_parameters` contains URL parameters
    
-   Method: `webhook_action.method` shows the HTTP verb used
    
-   Path: `webhook_action.path` shows the request path
    

Use Tines formulas to extract and transform this data in downstream actions:

<<webhook\_action.body.alert\_name>>

<<webhook\_action.headers.x-api-key>>

<<webhook\_action.query\_parameters.user\_id>>

## Sending the webhook request body as a response

When a request is made to a webhook action, the default response is "Ok". To change the response to send the request body instead, follow these steps:

First, add the response option to the webhook action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302415190/955623657d32ce71b8325320b1d3/webhook+response.gif?expires=1784786400&signature=b88100be344d945f37c6d54be6e61834dffb3f602e31ad430335ffc9eeb3d840&req=diMnFM1%2FmIBWWfMW3nq%2BgXRWO9A0hv5l%2BuSL0%2BR7jO4G2gHcEooVajUV%2FLOl%0ALyAMnuDx9bQICZhAjdhH%2B966YIE%3D%0A)

Then, add a value pill referencing the body of the webhook action using the syntax:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302416121/9efa5348a231765a12ee92aba54d/image.png?expires=1784786400&signature=5a3b8e4d70d7403aff11b904d369be13d465e327630049bf2fbe42ec2fdee494&req=diMnFM1%2Fm4BdWPMW3nq%2BgYvr5SDa51PoBRk6B%2BlWymb0agUtxvAifgyIHRXc%0AgJF69Kp0Hi6GHxTNC%2BFcEMCz718%3D%0A)

## Rate limits for webhooks

Individual webhook paths are restricted to 1,000 requests per minute.

The overall webhook rate limit per tenant is 2,500 requests per minute. If a tenant reaches this limit, webhooks will stop receiving events tenant-wide.

> Read more on webhooks in our docs [here](https://www.tines.com/docs/actions/types/webhook/).

## How usage in Tines is calculated

> **👉 For a full overview of our pricing and packaging, please [visit this article](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).**

### **Flows**

A connected set of one or more elements (actions or tools) that run autonomously. Running autonomously means the story kicks off on a schedule, via another system or story, via a case, or from a page submission.

If you have more than one story on a storyboard, ***each flow*** is counted independently towards your limit.

#### What doesn’t count towards a flow?

Any workflow or set of elements that requires you to run them manually from within Tines. This includes deactivated elements.

#### **Where can I monitor my flow usage?**

You can see how many flows are on your storyboard in the build pane. It also exists on your tenant-level reporting.

### Users

Users in tines are any unique email address that logs in to your Tines tenant, regardless of access level.

#### **What doesn’t count toward your user limit?**

Anyone interacting with Tines from outside the tenant, i.e. from a prompt or page.

### Teams

Learn about Teams [here](https://www.tines.com/docs/admin/teams/).

### Event Limits

#### **What are event limits?**

This is the monthly or daily limit for how many story runs can happen. The number resets at the end of each limit period (monthly or daily). [Learn more here in our docs](https://www.tines.com/docs/events/#daily-event-limits).

#### **What happens when I reach them?**

We’ll notify you when you’re 80% of your event limit via email. If you reach 100%, we’ll email you and stop the story that reached that limit.

When a story reaches its event limit, it will wait to run any other events until the next day. If it’s a runaway story, you can always contact our support team for assistance.

### AI run-time credits

[AI run-time credits](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits) are used to run Al-powered features in Tines like the Al Agent action and Workbench.

Community tenants are allocated 50 monthly AI run-time credits, which do not roll over to the next month. Users will not be able to top up Community edition tenants with more credits.

## HTTP Request Action 4xx Status Codes

## Overview

When working with APIs, 4xx status codes are common throughout all platforms. While the messaging may slightly differ between integrations, these typically indicate an issue with authentication.

The most common 4xx status codes you'll run into are:

-   `401 Unauthorized`: The server you are making the API call to doesn't recognize you.
    
    -   Real-life example: You're at the airport, but you haven't shown your boarding pass or ID at security. The officer says, "You can't go through until you show your documents."
        
-   `403 Forbidden`: The server you are making the API call to knows who you are, but you don't have the proper permissions execute it.
    
    -   Real life example: You’ve shown your boarding pass and ID at the airport, passed through security, but when you try to enter a restricted area like the cockpit of the plane, you're denied access since you are only a passenger.
        

> **Tines Tip**: For help with setting up integration credentials in Tines, take a look at our [authentication guides](https://explained.tines.com/en/collections/3801629-authentication-guides) or our built-in [connect flows](https://www.tines.com/docs/credentials/connect-flows).

## 4xx Status Codes: Cause and solution

### Cause One: The credential does not have the proper permissions

One of the most common reasons as to why an API call might return a 4xx status code is due to the credential that is in-use does not have the permissions necessary to execute the call.

> 🟢 **Solution:** Check your integration's documentation to ensure that your credential is set up correctly and is active.

### Cause Two: The credential is not formatted correctly

While the permissions of your credential may be correct, if it is incorrectly referenced within your HTTP Request action, a 4xx status code can still occur.

> 🟢 **Solution:** Check your integration's documentation to ensure that you are correctly referencing the credential within your API request. Two common examples are:
>
> 1.  Authorization Bearer Header:
>     
>     ![](https://downloads.intercomcdn.com/i/o/1056595200/73644eca1f249fa8bbabe69d/image.png?expires=1784851200&signature=1c7023173c61f2ebbf2e6ba69e136a1ad36b6ae59e0660928151667a4246fa08&req=dSAiEMx3mINfWfMW3nq%2BgXELypYhsFP8esKQ3QfCeWiLCRcTGzBiPnxb4QSg%0AjGB60Xc6JSm%2BVd7JUFQDl6GVyJ8%3D%0A)
>     
> 2.  Basic Auth:
>     
>     ![](https://downloads.intercomcdn.com/i/o/1056593693/6af9751f4bf1c40ca82a54a0/image.png?expires=1784851200&signature=919fa05e5ac8bfd304d007499960b85a5d50702c94822df99c636c8b4cb558bd&req=dSAiEMx3nodWWvMW3nq%2Bga%2FD4s2fSbqrIpFNnMsEdODfkRkVO8OKMMUUn%2FPa%0Ahvw8mMuunrEA0Q8MgwPrurH0YbU%3D%0A)

### Cause Three: The API method used is incorrect

Some credential permissions only allow for certain API methods.

> 🟢 **Solution:** Check your integration's documentation that the credential in-use can execute the API method you are attempting to call. Tines supports the following API methods: `DELETE`, `GET`, `PATCH`, `POST`, `PUT`.

## 4xx Status Codes: Troubleshooting checklist

☑️ Confirm the credential used has the proper permissions and is not expired.

☑️ Confirm the credential used is formatted as needed for the API call in-use.

☑️ Confirm you are using a credential appropriate for the API method you are calling.

☑️ Confirm you are able to run the API successfully outside of Tines (for example, in an API platform like Postman or through a cURL command).

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**, when available:

> ℹ️ **General Information:**
>
> -   Describe the behavior in detail, including steps to replicate (*we recommend providing this information via a screen prerecording*).
>     
> -   A documentation link to the API call you are attempting to make.
>     
>
> ℹ️ **Story Details:**
>
> -   The action configuration(s): You can copy + paste actions into a code block; this will provide the configuration of the action(s) in a JSON format so that Tines Support can use it into our test environments.

## HTTP request action example - Bulk request

## Overview

Some platforms offer API endpoints that handle **bulk requests**. A **bulk request** allows you to send multiple requests at once, rather than one by one. This is useful when you need to make many updates in a short period of time.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### **Scenario**

![](https://downloads.intercomcdn.com/i/o/am0g137l/1451060542/b261188efb9d62adf3df820deb4e/image.png?expires=1784851200&signature=249e2bfe5c797821bda028e25c4c179c6108b4a79003eea6e1d1cf1921f54692&req=dSQiF8l4nYRbW%2FMW3nq%2BgT4bozQ%2B6%2BYwZ4ENQU8RoCaxF4sb3Qh%2Fs3liLzc9%0AYXn9fm%2BOxM%2BmTf0KL1T70TWS1VA%3D%0A)

Invite multiple users at once to a message channel.

1.  In the storyboard, pull in an **HTTP Request** Action.
    
2.  **Name** the action something relevant, like “Get US Addresses.”
    
3.  Delete the **Payload** section of the action.
    
4.  For the **URL** of the action, type `https://tinestutor.com/webhook/1fff5794f8357ed4af7769e4af1d4272/0d31417ab3a249f9ed1a6e470cb64564?channel_id=C01MYA737L0&users=angie@tines.io,yanni@tines.io`.
    
5.  Change the **Method** of the action to `POST`.
    
6.  Click **Run** to run the action.
    
7.  See the results in the action’s **Events** panel.

## HTTP request action example - Filtered request

## Overview

A **filtered request** allows you to narrow down the data you’re asking for. This is useful when you're dealing with a database or service with a lot of data.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### Scenario

![](https://downloads.intercomcdn.com/i/o/am0g137l/1451064632/067e13a846518374f9555f132d21/image.png?expires=1784851200&signature=267401f8d40e0de334ec06e007a0f2fe9a044651ade94b733ab355becda972fb&req=dSQiF8l4mYdcW%2FMW3nq%2BgYKS5WhuAn7dUU%2BLPiMDX%2BN02x0iz29R28HrWtvS%0A7LxPyFGSgTrq2ZQkj0zkbtKx%2Bmg%3D%0A)

Instead of pulling a full list of addresses, only ask for US-based addresses.

1.  In the storyboard, pull in an **HTTP Request** Action.
    
2.  **Name** the action something relevant, like “Get US Addresses.”
    
3.  Delete the **Payload** section of the action.
    
4.  For the **URL** of the action, type `https://fakerapi.it/api/v2/addresses?_country_code=US`. Notice how we are filtering the request to only pull US addresses.
    
5.  Change the **Method** of the action to `GET`.
    
6.  Click **Run** to run the action.
    
7.  See the results in the action’s **Events** panel!

## HTTP request action example - Request with authorization

## Overview

**Authentication (also sometimes referred to as just Auth)** is required when the service you’re calling via an HTTP request action needs to know that you are allowed to access its data.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Credentials](https://www.tines.com/docs/credentials/)
    
-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### Scenario

![](https://downloads.intercomcdn.com/i/o/am0g137l/1451066553/0652976dabda1e88ffd5617206b7/image.png?expires=1784851200&signature=ccf6a5aef2b7afa2b6bc755b433fc54cb55e0b583748dcb4fc4c8fd39ff1a83a&req=dSQiF8l4m4RaWvMW3nq%2BgafXr%2Bhgp5cLgFZzsyG9KaSev6qVaHYpvU7VboON%0AMOKGVSlc17al7nq%2FkjA3VY9l3Xc%3D%0A)

Using the [Simple Weather API](https://www.weatherapi.com/api-explorer.aspx) (which requires an **API token**), ask for weather data for a specific city by providing the city's name or postal code.

1.  Navigate to your **Credentials** page.
    
2.  Click **\+ New** > **Simple Weather API**.
    
3.  Once the credential is created, navigate to your storyboard.
    
4.  In the storyboard, pull in an **HTTP Request** Action.
    
5.  **Name** the action something relevant, like “Get Dublin Weather.”
    
6.  Delete the **Payload** section of the action.
    
7.  For the **URL** of the action, type `https://v1.simple-weather-api.com/api/public/weather?location=Dublin&key=`. After the `key=` value, add a value pill via the **+** icon > **Value** option. Insert the following into the pill: `CREDENTIAL.simple_weather_api`.
    
8.  Change the **Method** of the action to `GET`.
    
9.  Click **Run** to run the action.
    
10.  See the results in the action’s **Events** panel!

## HTTP request action example - Request with parameters

## Overview

Sometimes, you need to add **parameters** (extra information) to your HTTP request to be more specific about what data you want returned.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### **Scenario**

[Watch video](https://www.youtube.com/embed/cvgCmtOqg7Y?rel=0)

Ask for the current price of Bitcoin in US dollars (USD).

1.  In the storyboard, pull in an **HTTP Request** Action.
    
2.  **Name** the action something relevant, like “Get USD Price of Bitcoin.”
    
3.  Delete the **Payload** section of the action.
    
4.  For the **URL** of the action, type `https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd`. Notice the parameters being passed in the URL after the question mark (`?`).
    
5.  Change the **Method** of the action to `GET`.
    
6.  Click **Run** to run the action.
    
7.  See the results in the action’s **Events** panel!

## HTTP request action example - Simple GET request

## Overview

Making a simple request can be something like asking another system for some basic information, for example, pulling a list of users or getting data from a database.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### **Scenario**

[Watch video](https://www.youtube.com/embed/kFKBEsIdlmE?rel=0)

Ask for a random cat fact.

1.  In the storyboard, pull in an **HTTP Request** Action.
    
2.  **Name** the action something relevant, like “Get a random cat fact.”
    
3.  Delete the **Payload** section of the action.
    
4.  For the **URL** of the action, type `https://catfact.ninja/fact`.
    
5.  Change the **Method** of the action to `GET`.
    
6.  Click **Run** to run the action.
    
7.  See the results in the action’s **Events** panel!

## HTTP request action example - Templatize request

## Overview

**Templatizing** a request means creating a reusable request template. Instead of writing the same request multiple times, you can create a generic template and fill in the details as needed.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Action templates](https://www.tines.com/docs/actions/templates/templates/)
    
-   [Tines - HTTP request action](https://www.tines.com/docs/actions/types/http-request/)
    

## Make it happen

### Scenario

![](https://downloads.intercomcdn.com/i/o/am0g137l/1451070293/a40966a9f6a6e13da14ea4f051e8/image.png?expires=1784851200&signature=f3b259dfcbf98c0855c4cf4d35675528ef3fa9dc1178955dc85f607e871f5486&req=dSQiF8l5nYNWWvMW3nq%2BgVIhYnDb%2Fs9ytZqsbe4J4U7xv42y7lbUdWzQQcjK%0AyDytVaNrlt%2BmRdbfJS3kc%2BVVgvI%3D%0A)

You might have a request template for inviting a new user, but every time you use it, you'd only need to change the user’s name or email address. To [save an action as a template](https://www.tines.com/docs/actions/templates/private-templates/) to use in the future:

1.  Click the action tile.
    
2.  Click the three-dot menu.
    
3.  Click **Create template**.

## HTTP request action example - Tines templates

## Overview

Tines offers thousands of templates for a vast array of 3rd party platforms via **[Templates](https://www.tines.com/docs/actions/templates/templates/)**.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Templates](https://www.tines.com/docs/actions/templates/templates/)
    

## Make it happen

### Scenario

[Watch video](https://www.youtube.com/embed/AxcHydexa_s?rel=0)

Need to list all users in your Slack workspace, but don't know where to start? Check **Templates** to see if Tines has already built the HTTP request to get this done!

1.  On the storyboard, navigate to **Templates**.
    
2.  Search for Slack.
    
3.  Drag the Slack tile onto the storyboard.
    
4.  In the right-hand search, type in “List all users”.
    
5.  Select the “List all users” template.
    
6.  Now, you can set up the action as needed to successfully run this API.

## HTTP request FAQs

## Are HTTP request actions sent using HTTPS?

Yes, HTTP request actions are sent using HTTPS.

## How can I see the whole URL used in an HTTP request?

You can check the details of an HTTP request using the test function in Tines.

Select the action, click "Test", then click "Log".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2207744361/e895d3d4ec0caf20e708f15af123/test+http+request+logs.gif?expires=1784851200&signature=4243e6dc2bc68a4c514241060ac3ae00d896730fb46590818e520f84fdab4056&req=diInEc56mYJZWPMW3nq%2BgbNqEcKIuXcmRoGGN3tWulPh2U5PFf0sSNLQSi5%2F%0ApCW4pgm6ijltby51ZTzCjMXlIz4%3D%0A)

The credentials will be redacted, and there is also an option to "Copy request as a cURL command" for debugging and troubleshooting.

## What is the max HTTP request size?

There is a 30 MB limit on HTTP requests.

## How does Tines handle API retries?

With the HTTP request action, you can use the "Retry on status" or the "Retries" options available for the action.

Select the HTTP request action and click "+ Option" to set these.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2209016661/a057667ff96fec1ac9cfc129ee6a/retries.gif?expires=1784851200&signature=4351cd758f3a41a784d229f58dc6f324c9fbfc1f0fb926dc8c29bd5d8f987c38&req=diInH8l%2Fm4dZWPMW3nq%2BgYPIux8af24BGhPq8rT9sTL6g02X0Vz2%2F9iGTZrM%0AastrBzYN3Ha6O0HsqhYXWUEJwZ4%3D%0A)

This lets you choose how many times to perform a retry on the HTTP request action if it fails the first time. Leverage the "retry on status" to determine what status codes it should attempt a retry - like 429. 429 is the status code for a service being busy.

When "retry on status" is enabled, Tines will silently attempt a retry up to 25 times, but this figure of 25 can be reduced using the "retries" field.

## Will an HTTP request emit an event if it returns an error or no response?

An HTTP request will emit an event if there is an error returned or a timeout.

You can configure the option to "Retry on Status" which is available by pressing "+ Option" on the properties panel.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2209021354/c2b60db43eca9d090cdb47e42867/retry-2Bon-2Bstatus.gif?expires=1784851200&signature=298d8c8a04eed95063c59ba87e53332024c580de5c10faa6fdac267e8f0d889c&req=diInH8l8nIJaXfMW3nq%2Bgdk5qrAU6OXzpCC8LUnMl5a6yWuz7LoYe5Vma1Hw%0A6jIGKmMMpHell7DfdPhdTRgF89k%3D%0A)

An HTTP request will emit an event if it successfully completes the call. If there is an error in the URL or the pill the HTTP request will not emit an event.

## How can I change the default timeout for an HTTP request action?

You can alter the time out for an HTTP request action in Tines by selecting the action, then on the properties panel, selecting "+ Option" and selecting "Timeout".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2207745771/818260d7c0991754d8d1280f915a/Timeout.gif?expires=1784851200&signature=a30e642adb473d5110357d6403fa421cefa1e9b10e7ae4f9d66858712077cb92&req=diInEc56mIZYWPMW3nq%2BgaA2qLP0VU0DTFpMlaTknfrajlV1LRwQ5SQTwXCZ%0ApbU3nDYWj4Sf0cdR37LEJEvAilE%3D%0A)

The default setting is 30 seconds.

> For more information on HTTP requests, read our docs [here](https://www.tines.com/docs/actions/types/http-request/).

## Hybrid Analysis Authentication Guide

Submit malware for free analysis with Falcon Sandbox and Hybrid Analysis technology. Hybrid Analysis develops and licenses analysis tools to fight malware.

## First, get a Hybrid Analysis API key

1.  Login to your Hybrid Analysis account  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910236612/5c3c5471e792f24ab3f49df8/image.png?expires=1784851200&signature=9d2c599bf74bb7a7d8f6293d9a66e1d3199aa58f86672a792bc07f27c341a2c7&req=fSEnFMp4m4BdFb4V1XW4gRLs%2BzDyCD9op5XmHbZgEjBLFnegfiEXJ1wIK9bk%0A7%2BTVGLzcIrcPd%2BY6X7cBeRBGrw%3D%3D%0A)
    
      
    ​
    
2.  Click on your user account and then click "Profile"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910237054/052830e65af4c367afa591f8/image.png?expires=1784851200&signature=0a02656488cdc72eb5049e7265a01212cf4aa8112879049b7e653f5ed5aa9e22&req=fSEnFMp5nYRbFb4V1XW4gZfxp55aNF6NqD2%2FE%2B9LDkkTGgQEdrVozeMruiGY%0AcWf7qUSn1b7ymnSVf2vKKTIQLg%3D%3D%0A)
    
      
    ​
    
3.  Click "API key" and "Generate an API key"
    
4.  Copy your API key
    
    ![](https://downloads.intercomcdn.com/i/o/910237948/791258e95d8c649c9fcdccf0/image.png?expires=1784851200&signature=eebac547c4978af825c098a42aa0f975d1682ac93446de1714628393a93525e5&req=fSEnFMp5lIVXFb4V1XW4gYHBGtBsYIzeJGvKuUeh1653katH4up2P8mI3HnY%0A6H7dnR2p8Hd3v18%2FiR05%2FrOI7A%3D%3D%0A)
    

## Then, create a Hybrid Analysis credential in Tines

**Hybrid Analysis connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Hybrid Analysis" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Hybrid Analysis connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Hybrid Analysis credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    
    1.  **Domains:** \*hybrid-analysis.com
        
    2.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/hybrid-analysis).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Hybrid Analysis** **stories in the [story library](https://www.tines.com/library?s=hybrid%20analysis&view=all)**

## If I use a Trigger with multiple rules, do all rules have to be matched or just one?

A trigger with multiple rules acts in an AND fashion. All rules must be satisfied for the action to work.

You can use “must match” in an individual trigger action to specify “OR” For example, using must match 1 acts as an OR.

## If JIT is enabled and SSO page restrictions are used, does assigning a user to the Tines SSO app also grant tenant access when viewing a page?

When JIT is enabled in a tenant and **SSO-group-based page access** for pages is enabled, Tines will not add a user to the tenant when they open a page as long as that user does not exist in an SSO group mapped with JIT. This is true for all page **Access control** options.

For example, if a user is assigned to the Tines app in Okta, but this user is *not* in any SSO groups, JIT will not add them to the tenant when they access a page, as there's no SSO group to associate them to.

> For more information on SSO in Tines, read here:
>
> -   [Pages - Controlling end-user access](https://www.tines.com/docs/pages/distribution-and-access-control/#controlling-end-user-access)
>     
> -   [Single sign-on](https://www.tines.com/docs/admin/single-sign-on/)
>     
> -   [JIT](https://www.tines.com/docs/admin/user-administration/jit/)

## Imperva WAF Authentication Guide

Imperva's Web Application Firewall (WAF) provides out-of-the-box security for your web applications. It detects and prevents cyber threats, ensuring seamless operations and peace of mind. Protect your digital assets with Imperva's robust, industry-leading solution.

You will need the following to create an Imperva WAF credential

-   Imperva WAF username
    
-   Imperva WAF password
    

## Create an Imperva WAF credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "HTTP Request"
    
4.  Input the values for the Imperva credential
    
    1.  **Name:** Imperva
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://{host:port}/SecureSphere/api/v1/auth/session`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** NA
        
    7.  Click "+ Add Option" and select "Basic Auth"
        
        1.  Replace `username` with your Imperva WAF username
            
        2.  Replace `password` with the value pill `<<secret>>`
            
    8.  **Secret:** Your Imperva WAF password
        
5.  Click "Save"
    
6.  Click "Run request" and double-click on the `session-id` key to copy the path.
    
7.  **Location of token from response:** Paste the path to the token in the value pill, `imperva.body.session-id`
    
8.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
9.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/956916681/ac7973be42c27c5a5055744c/image.png?expires=1784851200&signature=eac72cca3cfe9354cbd806e658c5565d0077fa0447f36510a73b7ab3d07b366d&req=fSUhH8h4m4leFb4V1XW4gYW91dc7NqquHSmUi7kGEPhTvhw9ipN8%2BmOyDAU4%0AJsHVYO0KQUtQBwHoJxHNFq8T4w%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/956917005/4ddec08131ab31205f6c182d/image.png?expires=1784851200&signature=5346a5afd15a97b170d69603760c1aceb0690c750c8e711d0b0c0324ecbfad4e&req=fSUhH8h5nYFaFb4V1XW4gcQkRZGeNuPkmRowNMOUPAtAH1cUmN3OlyT3UHVV%0AthoAYs8mlRKYBD1qcbgENV%2BpWQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/956917317/caa2da5f59c8e898c3eb1947/image.png?expires=1784851200&signature=11ddd91dcb08938dcad284ad66fed621c4578ebeef4d7bbb270ca6a4012bfe98&req=fSUhH8h5noBYFb4V1XW4geap17QCC%2F3avNeKhF2MoL8Slm8bYrZbIz8lTXPf%0Ak7wkShcbWvJxogiVA%2BoBIwT4rw%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Imperva WAF stories in the [story library](https://www.tines.com/library?view=all&s=imperva+waf).**

## Important change in connecting with Microsoft Teams

Recently, Microsoft announced it will be [retiring its Office 365 connectors within Microsoft Teams](https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/). Starting August 15, 2024, all new creations must be built using the Workflows app in Microsoft Teams. Existing connectors within all clouds will continue to work until December 2025; however, using connectors beyond December 31, 2024 will require additional action by the user.

Connectors, or webhooks, are essential to everyday workflows because they direct content and updates into a Teams channel, preventing users from needing to go to a third-party service for updates on alerts or notifications. We understand that many Tines customers have such webhooks set up in their stories that allow them to send messages from Tines to Teams.

## What can Tines builders do?

For builders that are currently sending messages from Tines to Teams via a webhook, you will need to [set up a Microsoft Teams credential](https://explained.tines.com/en/articles/8677501-microsoft-teams-authentication-guide) that communicates with the channel directly. From there, builders must update the action within their Tines story accordingly.

Follow our [Microsoft Teams Authentication Guide](https://explained.tines.com/en/articles/8677501-microsoft-teams-authentication-guide) for step-by-step instructions on creating a Microsoft Teams application with delegated permissions tied to a user or service account. If your organization requires a more custom Microsoft Teams credential, follow our [Microsoft Graph Authentication Guide](https://explained.tines.com/en/articles/8210263-microsoft-graph-authentication-guide) for steps to enable your preferred custom scopes.

Alternatively, Tines has many Teams templates available to pull into a story. As a starting template, our recommendation is to use “Send an adaptive cards message in a chat.” You can find this template by either searching through the Microsoft Teams templates or searching for the template within the storyboard command palette via `Cmd+K`. A chat ID is required, which will point to the individual or channel you wish to receive updates.

```
NOTE: if you are sending communications to Tines from Microsoft Teams today, no action is required.
```

## Importing and exporting stories

Along with creating stories from scratch, Tines also allows you to import and export stories.

Need a refresher on stories? Read [this article](https://explained.tines.com/en/articles/12688464-stories-in-tines) on stories in Tines.

## Importing a story

To import a story in Tines:

1.  In the top right-hand corner of the stories page, click the drop-down menu located next to the **New** button, and select **Import Story**.
    
2.  Upload your JSON file. You can either click to upload the file or drag and drop the JSON file into the upload area.
    
3.  (Optional) Rename your story during the import process.
    
4.  Click **Import**.
    

The imported story will open immediately, allowing you to continue building it.

This short video will walk you through the above steps:

[Watch video](https://www.youtube.com/embed/qYHJ3Ursd-U?rel=0)

### Version limits for importing story exports

There are no version limits. Tines maintains backward compatibility for story imports.

> Tines Tip: In Tines, the story export JSON file captures a snapshot of a story's configuration, enabling it to be re-imported into another team or tenant. This file's schema includes the necessary configurations for the story to run, excluding resources, credentials, and data generated by the story (i.e. action events).

## Exporting a story

To export a story:

1.  On your stories page, click the 3 dot icon on the story you want to export.
    
2.  Select **Export**.
    
3.  Check the boxes if you want to randomize page/webhook URLs and/or clear email addresses and monitoring recipients.
    
4.  Click **Export**.
    
5.  The file will download.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2222824821/3cfba763d16d09803428bd8095b3/Export+a+story.gif?expires=1784851200&signature=e74dfc17d5a6098a09b771825f61253dd186c4193a09345ff90b02f9e44eb579&req=diIlFMF8mYldWPMW3nq%2BgTtUJnKNTW0F68WWW%2F7DvJjiFjV47hheumZC%2BMjj%0A%2FR7nG2SIlENzVmxgo3xIgm7V%2Fd0%3D%0A)

### Export a story as an image

1.  Export your story as an image via the three-dot menu within your story.
    
2.  Select the "Save as image" option.
    
3.  Your file will download as a PNG file.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2222826446/bdd21907136089cd13bc9699a3f3/Export+story+as+image.gif?expires=1784851200&signature=1941065ffd751879fb9c764f3cbe574de171b29b32d3bad8832baee677a70629&req=diIlFMF8m4VbX%2FMW3nq%2BgSpsTspKWQY3X6YC3K53pTmlO2etLWpLRh11QT2a%0Ao3OpiF6j%2Bo7Ael63fH0DkIhaifA%3D%0A)

## Can't select your tenant when importing a story from the story library?

If you are using a self-hosted tenant or single sign-on for a cloud tenant, you may encounter a situation where you cannot select your tenant when importing a story from the story library.

To resolve this, either:

1.  Go to https://login.tines.com/saml\_idp/choose\_tenant
    
2.  Select switch tenant from the menu as below.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2302093998/46f74989b2dc83733c46653eed57/switch+tenant.gif?expires=1784851200&signature=3d29c16d7b34e554a41efce90af4a28b1525d8ddda7992db6dba6c8e32861cff&req=diMnFMl3nohWUfMW3nq%2BgdMLEGddd6WBTgbOX9SNUqnrmNwYzrx7QPlcexdm%0AE04M7QDOqpSchqwLZWg%2Ba7tUuzk%3D%0A)

-   Select "Add a self-hosted tenant" from the options available and enter your tenant ID. This will work for both self-hosted and cloud tenants if needed.
    

> Read more about stories in Tines in our docs [here](https://www.tines.com/docs/stories/).

## Infoblox BloxOne Authentication Guide

Infoblox delivers essential technology to enable customers to manage, control, and optimize DNS, DHCP, IPAM (DDI). Infoblox’s patented Grid™ technology helps businesses automate complex network control functions to reduce costs and increase security and uptime – building bulletproof, scalable, and efficient networks.

## First, get an Infoblox API key

1.  Login to your Infoblox account
    
2.  Click on the user menu and then click "User Profile"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/934370757/5315f69e6602c670666a40bf/image.png?expires=1784851200&signature=f0cb96a71a133e166a2d22987541d8f2ad455dc3214ef828fba4f6a4f1f0b087&req=fSMjFc5%2BmoRYFb4V1XW4gcqGgHHtgE8Zi7mxMZP7In%2BwPnNmHnfvkZ%2BsNqpw%0Ayk5RSHBnQF99J%2B2o5sQUkVezlg%3D%3D%0A)
    
      
    ​
    
3.  Click "User API Keys" and then click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/934371121/135fb266f61678021180b791/image.png?expires=1784851200&signature=63d57c46e830f1cffaf066cbae661dfd4c306e1e2699ac3338eab97863eef1b1&req=fSMjFc5%2FnINeFb4V1XW4gcVDQBnXWXp8Ab5dBSwdQ9ZnqXaBSyScb62NRavg%0AHikKLdQ%2FI7S9w%2BBnFGyvpJ2CEQ%3D%3D%0A)
    
4.  Name the API key, choose an expiration date, and click "Save & Close"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/934371660/def0ec97326129c58d9f5fbf/image.png?expires=1784851200&signature=22eb7e73acf5e48ef8b4658548210e6037a40e4fd039b1dd2c1f79b07ed86971&req=fSMjFc5%2Fm4dfFb4V1XW4geZ4lW9izuqjeMbUiZEGGHP6gpa0f8QAN7YLxJMa%0AxfrsfW0%2BRLB%2FrljsQjbPHPhWBQ%3D%3D%0A)
    
      
    ​
    
5.  Copy the API Key to a text editor and click "Close"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/934372116/73aa58fbb0b89bbb1b525c57/image.png?expires=1784851200&signature=994918736d90458499bf40579548ea9bcf631524098917116d377b276fdf7d85&req=fSMjFc58nIBZFb4V1XW4gRe5rIENM9u2F3SvcotrIb18HHzsgfY61rQw55Eq%0AkEgAYk9eHIZnzca5fyBpi7S5Zg%3D%3D%0A)
    

## Lastly, create an Infoblox credential in Tines

**InfoBlox BloxOne connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "InfoBlox BloxOne" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the InfoBlox BloxOne connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Infoblox credential
    
    1.  **Name:** Infoblox BloxOne
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
    4.  **Domains:** csp.infoblox.com
        
    5.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/infoblox-bloxone).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Infoblox** **stories in the [story library](https://www.tines.com/library?s=infoblox&view=all)**

## Integrating Microsoft Teams with Tines

App Studio had been used to create Apps in Microsoft Teams, but this has been deprecated by Microsoft. Users are now required to build apps via other means. This guide will walk you through how to create an app and bot using the Developer Portal app and the Azure Portal. Some steps require elevated privileges. These have been flagged to the best of our knowledge with the least-privileged access role required to complete the setup.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726589534/038a7e21f0f903e59cb7a31849ed/Untitled.png?expires=1784786400&signature=4f818a12bbdb55ab8c3a9ea7ac49b36175e64e056de0c8194fda39aad5c2d87b&req=dSclEMx2lIRcXfMW3nq%2BgW00mXk21tCCFDzcJ0KXDrIE1C6XK4AIDm2Vf76c%0AL9h6IxBoF7MaSxMgwYOST0UsjQg%3D%0A)

#### Checklist of items you will create in this guide:

##### Microsoft Teams:

1.  Application ID/Client ID for your App
    
2.  Bot ID for your Bot
    
3.  Client Secret for your Bot
    

##### Tines

1.  OAuth 2.0 Credential
    
2.  MS Teams Channel ID Resource
    
3.  Microsoft Service URL Resource
    

The process for building an App and Bot can be divided into 6 parts.

* * *

### Part 1 - Build your App

In Microsoft Teams, ensure you have the [Developer Portal App](https://teams.microsoft.com/l/app/14072831-8a2a-4f76-9294-057bf0b42a68?source=app-details-dialog) installed.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726627416/bf215194a75225e89fb3b301c34e/Untitled%2B-281-29.png?expires=1784786400&signature=483bd005d0ab90ae553ae4b3e75b420dae0761a19513ba56fffe752134b2c016&req=dSclEM98moVeX%2FMW3nq%2Bgb10dPlDvzrSTkCorxVYiEP4bC6NGB0GShA7BszG%0AR4aOfstBAKjPKjTPYS1J8YUET7s%3D%0A)

1a. Install the App, if necessary.

On successful install, you will be presented with the option to open the application:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726602492/58e0601ed20a56a2669731031e1a/Untitled+%282%29.png?expires=1784786400&signature=50233b52bb27661aedff4b23b5d597182610103997f88e0d32e2ce3a9244b095&req=dSclEM9%2Bn4VWW%2FMW3nq%2Bgd9zbcuQ4Td9sw7ELcWSTDn0adVpmG%2FfhFB%2FiqBF%0ACK61fQLxI3B5e2twWqIcHZy8Y%2BE%3D%0A)

2\. Open the [Developer Portal App](https://teams.microsoft.com/l/app/14072831-8a2a-4f76-9294-057bf0b42a68?source=app-details-dialog) then navigate to **Apps** tab.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726627415/076b52de399169b255094b0ebbb4/Untitled%2B-283-29.png?expires=1784786400&signature=1150c46e9b51dcc5a0e4a4cfafe23b6560218b56ade4e84546c1610759717596&req=dSclEM98moVeXPMW3nq%2BgViL%2F%2BhUAjZviqgGOp%2FhELKyxH%2BSMVRRPOtV02%2F9%0A9JQlQoqaFT6RhtK3pYsSwuuIKIo%3D%0A)

3\. Create a new App by clicking on the `+ New app` option. Give the app a name and click `Add`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726627414/eb9838a42e1b0a55759a23acbe98/Untitled%2B-284-29.png?expires=1784786400&signature=a26451996608890e54a14c438cdf85d02fd59c91d120d49f01dcc990863d8871&req=dSclEM98moVeXfMW3nq%2BgQM6T5mvDNzpUrdORqfnezzUffPACyTO5QN%2FJaCM%0A3sfb8s4UOTlCqDErzQu0FMaGk7Y%3D%0A)

4\. Fill out the following fields:

a. Short Description, Long Description, Developer Name, Website, Privacy Policy, Terms of Use, Application Client ID (should be the same as your App ID)

b. Examples:

1.  Short Description: `App for Tines automation`
    
2.  Long Description: `App which allows the Tines automation solution to send and receive Teams messages and adaptive cards.`
    
3.  Developer Name: `Tines`
    
4.  Website: `https://tines.com`
    
5.  Privacy Policy: `https://tines.com/privacy`
    
6.  Terms of Use: `https://tines.com/terms`
    
7.  Application (client) ID: `c8xxxxx7-5xx9-4xxb-yxx1-gk7xxxxxxx67`
    

c. Make a note of your `App ID`, then click on **Save**.

Optional Step: Branding

Upload your branding (logo, icons, etc), if desired. Note the mandatory requirements for pixel size and transparency.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726608084/1f2accb50c788b033aeb1ec492e2/Untitled+%285%29.png?expires=1784786400&signature=8842576e4230eac7bd4bff3522a692d1301b8455e0aae95504dcfb74f6122d37&req=dSclEM9%2BlYFXXfMW3nq%2BgQnI4X4ctMiuQLKsxjxZ7GF42XudExsmHowjrFEZ%0AFqce5hB4B5%2FLFxhbSYUDhivwnOk%3D%0A)

### Part 2 - Create a Bot to add to your App

> 💡 These steps require elevated privileges. To create a bot, you will need at least the [Application Developer](https://docs.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#application-developer) permission.

1\. In the Developer Portal, navigate to **Tools** tab and select **Bot Management**.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630199/6e53c1485312fa62c5e9afe54d5e/Untitled%2B-286-29.png?expires=1784786400&signature=38af003734e16e876daede729dc368ef211ce7dac4e1a8454e305dbb53df971d&req=dSclEM99nYBWUPMW3nq%2BgVFfDUIec7UKnNviUbS%2FLifDOyldwWpG0ykn9K8A%0AbcaA2hjl8dzahkIE8zIh0C3FPMk%3D%0A)

2\. Click `+ New Bot`. Give the bot a name and click `Add`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630202/941f66cab25e42acf1381fd4ab4e/Untitled%2B-287-29.png?expires=1784786400&signature=1af4feb9d9d3494b5ff4b4343fe3950c23d39d23c69fbbd12243c1ef6d1df3c4&req=dSclEM99nYNfW%2FMW3nq%2BgbMyWb9ZF49vpoc%2BtdUEGSuqQe1RUR8w8CI%2F3qta%0Aq3zRAh4KgaKYMf0E%2F%2Bc5U7YlxpQ%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630197/97506b38ce92b19019d67024806e/Untitled%2B-288-29.png?expires=1784786400&signature=e4af189cd7ba5276e5b36fb29911782241415fbf961bcffef7944211cee7e768&req=dSclEM99nYBWXvMW3nq%2BgR3dRwT2X2Sael7ysiYtDpvdHa864R%2F4gqzkTTdp%0APyR5hYO6ni0uQZK7M8vi%2BURwQLI%3D%0A)

3\. On successful creation of the bot you should see a message displayed with your bot name.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630201/0ae29250e595b3f27a3f2dd1b4d0/Untitled%2B-289-29.png?expires=1784786400&signature=fa918a01587697ddaaf29e17ea4633f94c113494a574c061b113f98cbc4ad961&req=dSclEM99nYNfWPMW3nq%2BgcJxbqYAH92dFlCvIa%2BKpyj0ZxGZkOjhn%2BEXQLue%0AciN2claxyMK52FqqUuilwKnvb6s%3D%0A)

4\. Navigate to **`Client secrets`,** then click **Add a client secret for your bot.**

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630196/23995094050b2bd5a070ed0d8fff/Untitled%2B-2810-29.png?expires=1784786400&signature=df8cda86b052493e42b17a35e6c70bda479273a104074934c01f94b4b6ab9a78&req=dSclEM99nYBWX%2FMW3nq%2BgUdXhLmZS3LPykc%2FtDMUQbnFoi7dBhV6xDV%2Bt2XH%0A6prlK3mTiRFO8u9PPSisIdG0Ono%3D%0A)

5\. A new client secret will be generated. Make a note of this, as it is the **only time** the secret displays. This will be needed to configure the [OAuth2 credential in Tines](https://explained.tines.com/053303244f8b4653b05043da274e3b87#eae6f7418d4944b698b20847fd5fcb67). After you have noted it, click **OK**.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630207/37638b27eb31ecc670b64d4b9509/Untitled%2B-2811-29.png?expires=1784786400&signature=c9c258a318cbf1f7e4498153ee23bc77fd3dad16d25704ffe640840a8004f050&req=dSclEM99nYNfXvMW3nq%2BgWIlff62bpK52k82Xv0Alxhq6wq8d9BmfhWrfFJv%0AhOwKpQocOQBsOBeAJWfU6wLYmHU%3D%0A)

6\. Navigate back to the `Bots` tab and make a note of your `Bot ID`. This will be needed to configure the [OAuth2 credential in Tines](https://explained.tines.com/053303244f8b4653b05043da274e3b87#eae6f7418d4944b698b20847fd5fcb67).

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630225/e0f1e922d32cce0c715f05fc8af2/Untitled%2B-2812-29.png?expires=1784786400&signature=7d7abfba931ef4ef49b8e8386539bde1352996a8773e21347a1fadfd35017c56&req=dSclEM99nYNdXPMW3nq%2BgS7L61F%2FHYgNaeUyhCg8uOVsFFdlsktlJlBGFo8q%0A3%2Fg7sSE4bd5xnVPkCCoZDKJ%2FkiI%3D%0A)

7\. Click into your Bot and navigate to **`Client Secrets`** and click on the [Azure link](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps) which will take you to an `Application Registration Page` in your Azure Portal.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630200/3766f42a70ce5714bd27a3794ea1/Untitled%2B-2813-29.png?expires=1784786400&signature=8bce5df833588d20074b6d14798f950b0ffca3f9b17267bb1068eaacc9646eb5&req=dSclEM99nYNfWfMW3nq%2BgXhFx%2BhqzSrv9jCXf56JGmj7btQouY%2BOIxgQxlf3%0AjYnB73IPa7K5mSpM2GYRNhH9GPg%3D%0A)

8\. In the Azure Portal for App registrations, select your Bot with the corresponding `Bot ID` noted from [Step 6](https://explained.tines.com/053303244f8b4653b05043da274e3b87#c10a17ff333c4159b24f144061edf431). Click the Display name and it will take you to the app’s `Overview`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630208/4d8c5700017c50cb5f69ebbeb56f/Untitled%2B-2814-29.png?expires=1784786400&signature=3472defa97c506ba7265a39b5152259ef19c9f27ac3b2f0c9fd7aabb03e845ae&req=dSclEM99nYNfUfMW3nq%2BgV6CDa2dszn%2B9udYuV8Ecnb58GM9ThhNALwOOE0n%0AQwLHsEwhF5a20REfecnhN9e8YTM%3D%0A)

9\. In the `Essentials` section, click on the `Add a Redirect URI` link.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630210/c0a6af9b848713f0dca34b64b24e/Untitled%2B-2815-29.png?expires=1784786400&signature=fd05a256b0ac1ebffcc8d31ce98e389ceef1cbada97d35410af39baaab4bb528&req=dSclEM99nYNeWfMW3nq%2BgbbtwToCfgG3eiYUn%2F0VhSnyeu2JcYyOc8ChWyjZ%0AeQg4tQqOLfdjQMtsIaI2aEQNqzI%3D%0A)

10\. You will be brought to `Platform configurations`. Click `Add a platform`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630215/c872a988d90e1a93ef1d8c073f8a/Untitled%2B-2816-29.png?expires=1784786400&signature=cfd073bd695c560de931f7bfadee76825b4147c60b772f53d14e323999724dc5&req=dSclEM99nYNeXPMW3nq%2BgTX6LOCjeVcM6bdvzgFv%2FRh%2BkMMI%2BMMg4HzP1fB8%0AjgPEUXTorpYvjATh111xrajBYdQ%3D%0A)

11\. Click `Web` under `Web applications`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630211/376d6dded9532ea1a829a7fd1934/Untitled%2B-2817-29.png?expires=1784786400&signature=b4536780a3e5e106e3e03231ec809d0ff10f0d9ae07e8a36a615fcd504b91646&req=dSclEM99nYNeWPMW3nq%2Bgagffkrw4KOh%2FsXmBEGN%2Fqbg5wOA2WFEgB41L43W%0AMbGxH9C7LiElDwJF1b2pnfkxgW4%3D%0A)

12\. In the `Redirect URI` field, paste your OAuth2 callback URI. You can find this in the

`Callback URL` section in a [Tines OAuth 2.0 credential](https://www.tines.com/docs/credentials/oauth/). You will be creating one for this bot [later on](https://explained.tines.com/053303244f8b4653b05043da274e3b87#eae6f7418d4944b698b20847fd5fcb67). Click **Configure.** The remaining fields can be left blank.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630203/35e8dd3a2a29b5a004a2b325aee8/Untitled%2B-2818-29.png?expires=1784786400&signature=8b305eff56cbaa319ba017ca28be404f910dec78dd4b1201d619db3addd658b0&req=dSclEM99nYNfWvMW3nq%2BgVBgtQDcvMaF9ZkXDRZzpmNFOW2857C2VsEY4rAp%0A5ITKxPtWEmizAZ9lngcDyOem6iY%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630209/4a37b699b50abbe392e99c5a161e/Untitled%2B-2819-29.png?expires=1784786400&signature=495e38fd833ceea0dfebc30f7245de5273ff0b2029c5d027ebf74050248fd8bc&req=dSclEM99nYNfUPMW3nq%2BgUq4FTo%2FTWl7FMvIPim3x%2BHiwPH80NcwB2L8376%2F%0AFWtAsC4EtpCMfT44NBvRxmRlVEY%3D%0A)

13\. You should now see the Web platform configuration for your Bot is redirecting to your Tines OAuth2 callback URI.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726630216/18a408d52b001b3a694a4fe65100/Untitled%2B-2820-29.png?expires=1784786400&signature=8e5811d55be4fc41342d4d52b48a9940db0763b1db428dd02604f3a32b899967&req=dSclEM99nYNeX%2FMW3nq%2BgQjjHnNAeCWwpX6brbd8x6Jmejjn%2BwQVKeb6%2F7VB%0AXjSG4Hco9XhrM0%2Bp4Q8f0Q5StOQ%3D%0A)

### Part 3 - Associate your Bot and Publish your App

> 💡 Some of these steps require elevated privileges. Minimum recommended role: [Teams Administrator](https://docs.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#teams-administrator)

1\. Return to Teams, access the Developer Portal App, and navigate to the `Apps` tab.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726639239/160eea0649924654e394007291e3/Untitled+%2821%29.png?expires=1784786400&signature=38d3e8394ce42598e062c5d408814e1134d6d1daf605e8f48dd3606b0a75a2cf&req=dSclEM99lINcUPMW3nq%2Bgfwjk%2BcY%2FdrCH9ZLthPPwUPuP45EK0OcPNwtMMGF%0Arf0GvU1nUlkCFY1Jbd9v9AbHc70%3D%0A)

2\. Select your App, click `App Features`, then click `Bot`.

3\. Select your `Bot` based on your `Bot ID` that you have noted from Part 2, Step 6. You can choose from the drop-down menu or copy and paste the Bot ID. Add the following scopes to your bot and **Save** the settings:

-   Personal
    
-   Team
    
-   Group Chat
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726644109/e52f1452066e79a0f9d5b5940fb1/Untitled+%2823%29.png?expires=1784786400&signature=3baeb402a7a97bf850dcc82388089e1de06cc0a516e7b64c87b31c2c82515522&req=dSclEM96mYBfUPMW3nq%2BgfZAcKzXeFBJsVLJ5BlDfVatpaRB9g7ix%2BPPRUKD%0AqzzQWm37EQQBWfZMLz8jqIBngNo%3D%0A)

4\. You will see a message displayed that your App has updated successfully. Click

`Publish` to publish your App.

5\. Click the option for `Publish to your org`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726646286/5342bd9dc18502597620c4ebe41c/Untitled+%2825%29.png?expires=1784786400&signature=f7608fa8555b2f02f9163cc0c6cee1906147f88928c15e447c6e51796427daa4&req=dSclEM96m4NXX%2FMW3nq%2BgUmq6DFW6zshD71u%2FSjtwvtrVpconzetkXkZAA6F%0ACmapKpXQMFMnx%2BdJsnkbHtx%2Fgnw%3D%0A)

6\. Click `Publish your app`. Within a few moments, you will see a Version number and a `Submitted` status.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726647791/546d0329d99762a801060b7329ef/Untitled+%2826%29.png?expires=1784786400&signature=9da3c0d42516022174dc7dedb2bd53a11e8f233036d4e1dc05f75021bc9e78ef&req=dSclEM96moZWWPMW3nq%2BgcDr9KPMTMl16SNjpWLA%2B7maDjOU3DnC0O8aRssZ%0AuzEi5FBB00DqMb7G1L8Bqh9fyVE%3D%0A)

7\. Navigate directly or ask your Teams Admin to navigate to the [Manage Apps section of the Teams admin center](https://admin.teams.microsoft.com/policies/manage-apps). Search for your App by its name in the

`Search by name` field.

8\. Click the name of your App to reach its About page. You will see a warning icon for `Pending Action` near the `Publish` button.

9\. Click `Publish` to publish the app to your org. You will see a confirmation message stating it may take a few hours for the app to appear.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726652233/d7bf0ed30838657e457904cdba6a/Untitled+%2830%29.png?expires=1784786400&signature=d5dd7c57c45d31fddb96092a19a395e1c158e818c15e879ca5e9b0d816a0ac85&req=dSclEM97n4NcWvMW3nq%2BgaZComictTZdUFzBZTNcn31SLf6lywDfxQbfnJMd%0AFud3ahCIwGw%2FUuVBMdXftUWOU04%3D%0A)

### Part 4 - Add your App to Teams

1\. Navigate to the `Built for your org section` of Apps in Teams.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726655845/e42779bbae427923870d75511e4c/CleanShot+2025-09-15+at+14_03_34%402x.png?expires=1784786400&signature=8b92b759f2b1c2c77e997d1a9f1571d9466f5ee95f3f8a8941ad9dc6ee4777ca&req=dSclEM97mIlbXPMW3nq%2BgQXbTA9Uf6IqiG7k%2BwIsS8h2M9xSzdfOSURu2wMS%0AWCUCP6BBiCPuaoYrLQ3qE5bVFf8%3D%0A)

2\. Click on your App and choose `Add to a team` from the dropdown next to `Add` or `Open`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726658076/cba392784fe7a488a156cc692a2c/Untitled+%2833%29.png?expires=1784786400&signature=3cc3124f51bba004b2a95b900e22d8e527c6f4c076a4bb4a58ac1fc80078861e&req=dSclEM97lYFYX%2FMW3nq%2BgTpetJ2sgcbXCkT8SlI01xEHVlBudfAJON0rOdMW%0Aog%2FqvZzy1UQDk5uZtHH5%2BOvn5iQ%3D%0A)

3\. Type the name of the channel where the App should be added to and click `Set up a Bot` to confirm. The App is added to the `Apps` tab in the `Manage Team` section.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726664364/0ae18286e319218633bfc884dcc4/Untitled%2B-2834-29.png?expires=1784786400&signature=0a120cf1e35ce7817338e05bd1014125ba214bd3ea0d73986fc1dad9894ba8d9&req=dSclEM94mYJZXfMW3nq%2BgahWyTjNJYi3zgufdrZzH%2FW4yyB9z%2F2f42qDKJ7a%0A4FifgHHwrjLL2J83l%2F6Nr4MksrY%3D%0A)

4\. Navigate back to your channel to retrieve the Channel ID. Click the ellipsis `...` , then `Get Link to Channel`.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726664367/f25b1bc89031ef4c21a86dad55d4/Untitled%2B-2835-29.png?expires=1784786400&signature=c279d826f17cf28d771c5edce15500f4b2c81c162493d5258318392dd758b992&req=dSclEM94mYJZXvMW3nq%2BgVG9YADL6pX%2BKzm0x84SLiGOywgNM7iq13OfLH8o%0AgPVKMoVDbsBzWrnohvT3wp2nxDY%3D%0A)

5\. Click `Copy` to retrieve the link, and paste it in a text editor of your choosing. The link should look something like this:

`https://teams.microsoft.com/l/channel/12%3Ld1zc48f07ae123b560ff3e67ab1234f7%40thread.skype/Event%2520Demo%2520Channel?groupId=e0123454-22ab-57j3-8yz0-dcbabcad8eb1&tenantId=63a1b2c3-123a-4567-ef89-7a5bc101867k`

We are interested in the value between `/channel/` and `/Event` , so from this example, the channel ID would be `12%3Ld1zc48f07ae123b560ff3e67ab1234f7%40thread.skype`.

Make note of your channel ID as it will be needed later on.

### Part 5 - Putting the pieces together in your Tines Tenant

1\. Create an OAuth 2.0 Credential in your Tines tenant with the following details:

-   **Name**: A name of your choosing, something like `MSTeamsBot`
    
-   **Type**: OAuth 2.0
    
-   **Callback URL**: Pre-populated
    
-   Client ID: Your [Bot Application ID](https://www.notion.so/Microsoft-Teams-App-and-Bot-Creation-A-Guide-for-Tines-Users-053303244f8b4653b05043da274e3b87?pvs=21)
    
-   **Client Secret**: Your [Client Secret for the Bot](https://www.notion.so/Microsoft-Teams-App-and-Bot-Creation-A-Guide-for-Tines-Users-053303244f8b4653b05043da274e3b87?pvs=21)
    
-   **Scope**: `https://api.botframework.com/.default`
    
-   **Grant type**: Client credentials
    
-   **OAuth token URL**: `https://login.microsoftonline.com/<<TENANT_ID>>/oauth2/v2.0/token`
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/1800362657/d517746162835a686f19ae37d7f6/credentialForTeams.png?expires=1784786400&signature=d90d4978285b8829726f2e5f2b0d31613fa2e68609c665e4f5f35f3763b061f3&req=dSgnFsp4n4daXvMW3nq%2BgbTkaSNQ2M7ugXmI8R4u%2BYCWP38C2Fl5EWAag%2FBI%0AegZQ7d06ay1cgz4bSPwowQVzW80%3D%0A)

2\. Create the following resources in your Tines tenant:

-   [MS Teams Channel ID](https://www.notion.so/Microsoft-Teams-App-and-Bot-Creation-A-Guide-for-Tines-Users-053303244f8b4653b05043da274e3b87?pvs=21)
    
-   [Microsoft Service URL](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#base-uri)
    
    -   This will vary depending on region and is [set by the channel](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#activity-object) or user location
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726676275/59677a6280696a217f788ca2ca03/Untitled+%2838%29.png?expires=1784786400&signature=e51763ae7bf1fa886b7304516f4d98f670c3052732bc980da410e46351d1caff&req=dSclEM95m4NYXPMW3nq%2BgdFo836gjEjxhZXVcH7wZwEKc8fPf1jiAxLgpipu%0AFVWA7ywjFTbzlXyNd9PK8l1KrD4%3D%0A)

### Part 6 - Testing in a Tines Story

1.  Copy and Paste the following JSON for the `Fetch Team Roster` action to your story, replacing the following:
    

-   Resources
    
    -   `microsoft_service_url`
        
    -   `ms_teams_channel_id`
        
-   Credential
    
    -   `MSTeamsBot`
        

```
{"agents":[{"disabled":false,"name":"Fetch Team Roster","options":"{\"url\":\"https://<<RESOURCE.microsoft_service_url>>/v3/conversations/<<RESOURCE.ms_teams_channel_id>>/members\",\"content_type\":\"json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.MSTeamsBot>>\"}}","position":{"x":345,"y":-150},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null}],"links":[],"diagramNotes":[]}
```

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726682305/40b05dd296b43bf80dbdd03b80f1/Untitled+%2840%29.png?expires=1784786400&signature=976178c4e2f3152e2c5e1d13250c04b097671ae2f86451f5664100634978719a&req=dSclEM92n4JfXPMW3nq%2BgeuHvuU7y4Xnzar0wuNTHPWR%2B4S7WdDjPDRdcGHr%0AMqqXHmPMllLOK%2FmFqZ%2FeljNfUbA%3D%0A)

2\. Run the action to fetch all the members in the channel whose ID you provided. Congratulations, you’ve just set up your Bot successfully with Microsoft Teams and integrated with Tines!

![](https://downloads.intercomcdn.com/i/o/am0g137l/1726684390/b3addf5af1eb067e8e9154865443/Untitled+%2841%29.png?expires=1784786400&signature=d31a474160d9283d691b1f52bd21caba9a21183d0c264b7cc6f9def6011a767d&req=dSclEM92mYJWWfMW3nq%2BgUwPKwU1%2F5JMjqOfz8Hc4RXScxuJsqPTSkMyzSLm%0A4Xa3hJas%2FAq1AFdkVFqAITfBoCI%3D%0A)

####

## Integrating Slack with Tines

## Overview

Using a Tines webhook, you can kick off your story by monitoring for relevant Slack events with Slack's **Event Subscriptions** app setting.

## Tines manual reference

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   Tines Action Types: [Webhook](https://www.tines.com/docs/actions/types/webhook)
    
-   Slack Event API: [Respond to the challenge](https://api.slack.com/apis/connections/events-api#challenge)
    

## Make it happen

1.  Within your Slack app configuration, navigate to **Features** → **Event Subscriptions**:
    
    ![](https://downloads.intercomcdn.com/i/o/993006827/0103bee01eedc50f3246d243/Screenshot+2024-03-15+at+2_49_46+PM.png?expires=1784786400&signature=69c1e246b263c6839b1bb00505df35937f08cdf10c90a3e1d88a588a636c9d8f&req=fSkkFsl4lYNYFb4V1XW4gRKyvjEjWr0HK8aG3%2Bl%2F11G6sBniDok22IVaavAV%0AZ1aZboluYgVKPJF8gTAL3fkELA%3D%3D%0A)
    
2.  Toggle on **Enable Events**:
    
    ![](https://downloads.intercomcdn.com/i/o/993013663/84b18eeec5c32169b8592ca6/Screenshot+2024-03-15+at+2_57_47+PM.png?expires=1784786400&signature=a5cd25a29d0a43788e65f2c6bb67c7b337ffd37cf0c8d25036523d7fbb4a1357&req=fSkkFsh9m4dcFb4V1XW4gUhvWo1w1op6MuS7F8mV9lhWcyYxhb9nBNj6xd1k%0ARRfdAKxhR9S0OrY5v7Rg8CbP9w%3D%3D%0A)
    
3.  Within Tines, drag a **Webhook** action onto your storyboard.
    
4.  Within the Webhook action, click the **\+ Option** button.
    
5.  Add **Response** and **Response Code**.
    
    ![](https://downloads.intercomcdn.com/i/o/993009377/d6fa428dcfa94720c4950497/Screenshot+2024-03-15+at+2_51_58+PM.png?expires=1784786400&signature=b1ecb202d582ab04b740c970f41982c39dfd7cc34a708f2fe5accd3cbaf200e7&req=fSkkFsl3noZYFb4V1XW4gYPwjysZpN4wMBQhqOE2Ed5UfeZjGZZcbtd0hE4a%0AfGRN2GwBrWNvKHndYQBmsRYvJw%3D%3D%0A)
    
6.  For **Response**: Set this in a pill: `body.challenge`
    
7.  For **Response Code**: Set to `200`
    
    ![](https://downloads.intercomcdn.com/i/o/993026599/73888768575eed82643665ee/Screenshot+2024-03-15+at+3_15_00+PM.png?expires=1784786400&signature=cf391ecc1cbbe7a25f242461d9f8fa2f9c7d3b9940c2d59fd2fd132582d85f05&req=fSkkFst4mIhWFb4V1XW4gWWIS5Kg2GKS5R4I%2F9awixxZ9pGB5JS2kmiWdbiA%0A3D5QQp5FaoF8mbcXjh%2BEcCbQ4Q%3D%3D%0A)
    
8.  Within the Webhook action, copy the **Webhook URL**.
    
9.  Back in the **Event Subscriptions** page of your Slack app, paste the **Webhook URL** into the **Request URL** box.
    

## Review the results

Slack will send a `url_verification` call to the associated webhook within Tines:

![](https://downloads.intercomcdn.com/i/o/993022906/c4783003dd65f7884b7294a3/Screenshot+2024-03-15+at+3_08_23+PM.png?expires=1784786400&signature=d012e7f22f912a87fe33e6a111b34db3b0cae274ecaac00d4a893d08918da950&req=fSkkFst8lIFZFb4V1XW4gbW4GXuL4n9f5IPrDHek73m2qrn%2BNb8EmHRLt9Wb%0AjOA3BemJk8lPunwv4CMJwtt56A%3D%3D%0A)

Tines will send the challenge value in the webhook's **response** back to your Slack app:

![](https://downloads.intercomcdn.com/i/o/993025529/9d90f566a37d2fc7531b73fc/Screenshot+2024-03-15+at+3_12_04+PM.png?expires=1784786400&signature=eee2f6f25e9fc6d7d7b578fa0cbd07b6d5c1248f7b6e1790d4534bd80d11edbc&req=fSkkFst7mINWFb4V1XW4gYvM1MXc2cPm5ta%2Fw1EOS23s3AiZRe0khRE%2BewZI%0AA%2B5M2wXgzYImI6kN3%2Far110iig%3D%3D%0A)

Thus, verifying the connection:

![](https://downloads.intercomcdn.com/i/o/993020456/8b78ffc22793ebd370e56fd6/Screenshot+2024-03-15+at+3_05_43+PM.png?expires=1784786400&signature=2bafb830f195a91b51be065f773dddc83cb106a8dc513ad55b7c0d8fd8e40358&req=fSkkFst%2BmYRZFb4V1XW4gRg1IUAo1y2cVne4THnWhed5OVEWpVoZr72njPim%0ApSFwIxU4OfIGd1IwFE5TTY9Vvw%3D%3D%0A)

## Intercom Authentication Guide

Intercom helps support teams streamline workflows, manage conversation volumes, and optimize resources.

## First, create an Intercom app

1.  Navigate to your Intercom Developer Hub `https://app.intercom.com/a/apps/<<workspace_id>>/developer-hub`
    
2.  Click "New app"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/879857293/429ac7cd0ca96df19ccb3e97/image.png?expires=1784851200&signature=c6ae1b5203d97da302dc97f6b637a112a4992e2761023ea803db65faf3eb2c17&req=fCcuHsx5n4hcFb4V1XW4gZt0cktFPiiF%2FcEHuQcMLdsSxuaqIyB5lW%2FtjGZw%0A7jREkK2gREHxsvIrHt7qfPwvnw%3D%3D%0A)
    
      
    ​
    
3.  Name your app, select your "Workspace" and click "Create app"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/879857492/4bd8ffef3bf9907e4ce19801/image.png?expires=1784851200&signature=0b2278efdfdaa1c8170e72ed0728226eb5e494c3c6f0e70965d5f332774bc976&req=fCcuHsx5mYhdFb4V1XW4gdpAGwYCLM0upksu5fpOop4pPR2WoeQ%2B8qEuLPEK%0Afh5QtuejOsEDofpRZAZdYxa0wg%3D%3D%0A)
    
      
    ​
    
4.  Copy your "Access token" to a text editor and select the required permissions
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/879857838/44cfda1becb9b5490f0f6afa/image.png?expires=1784851200&signature=2892c342250efba2609b5583eedb36a0947b72867bd2191f2fe095074862cba3&req=fCcuHsx5lYJXFb4V1XW4geyKa23Ta%2BP7AgK5mgLtQzZ5%2FvZiBTiNyX5t5nTZ%0ATv4WGwLSdMiPFeO6hvEidLw7Ww%3D%3D%0A)
    
      
    ​
    

## Lastly, create an Intercom credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Intercom credential
    
    1.  **Name:** Intercom
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Access token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Intercom** **stories in the [story library](https://www.tines.com/library?s=intercom&view=all)**

## Introduction to AI Agents

AI Agents can be customized to fit your specific business needs, helping streamline collaboration across departments, without adding to your team's workload.

**Check out the demo from Stephen, our head of product 👇🏼**

[Watch video](https://fast.wistia.net/embed/iframe/yen5oyj0qb)

> **Note:**
>
> 1.  As of February 10th, 2026, The AI Agent action is available on all Tines tenants. Existing customers please contact your account team to learn more about these features.
>     
> 2.  Using the AI Agent action will use AI run-time credits. You can see your credits used to date by each team in the AI settings modal. Learn more about AI run-time credits [here](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits).

> **Want to see it in action?** [Book your AI Agents demo now](https://www.tines.com/book-an-agents-demo/)

The AI Agent action is an evolution of the AI Action, complete with two modes: **Task** and **Chat**.

With **Task**, you can define and run a prompt within your story. You can also introduce tools to take action on your behalf.

Tasks are great for more straightforward actions that don’t need user input. You tell the task what it should do, it thinks and responds.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1583714038/f45ff1697aa3cb3b8ee90ac0e38e/CleanShot+2025-06-23+at+10_11_17%402x.png?expires=1784851200&signature=a8b8956886d81c8395e1bfabcfe674b058e041af392fa8c3bf7608e7e60bc56b&req=dSUvFc5%2FmYFcUfMW3nq%2BgYKza3sinJ0x743OiVyYjUniPfSPk%2FHr8Gl0pVkU%0AgEhtW3X5SykbTDAmU%2FFzK2rgj7Y%3D%0A)

**Chat** allows you to create your own AI chat interface for audiences inside your organization. They're great for situations that benefit from a back-and-forth conversation, one that needs clarification and confirmation with the end-user.

If you want an end-user to start a conversation with the agent, you can share a direct link with them. But chat links can also be sent automatically in a workflow to kick off the conversation proactively.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1583720378/de1ff0e15f63cb1d43a8bd01524b/CleanShot+2025-06-23+at+10_54_17%402x.png?expires=1784851200&signature=514159440c901696f0ccde258ea274bb73e2420fa50ac64b34f4c52abc58a39b&req=dSUvFc58nYJYUfMW3nq%2BgV6al6i3WmgXI2EV%2Bek0XyYOujKlk2ICcYWAGzQG%0APWU5ZQWZpad4by3ioV36YNuIl%2BQ%3D%0A)

You've always had the tools for deterministic workflows, now you're gaining the power of agentic workflows where you already build, run, and monitor your most important workflows!

* * *

## Resources

**AI Agent action FAQs**

All your burning questions about agents answered [here](https://explained.tines.com/en/articles/11510164-ai-agents-faqs).

**How should I configure and use agents?**

Take a look at our best practices for using agents in Tines [here](https://explained.tines.com/en/articles/11644147-best-practices-for-the-ai-agent-action).

**Where can I find additional resources on agents?**

We've gathered some helpful Explained articles together [here](https://explained.tines.com/en/collections/9473753-ai-in-tines).

## Introduction to cases

Cases is our solution for case management. Automation works with established processes to respond to threats or problems, while cases gives builders a place to solve for the new or unknown. A case in Tines is a collaborative user interface where you can comment, take action, and report on progress all in real-time.

> **Note:** Cases are an add-on for Tines customers. All customers on a paid plan may opt into a ***one-time*** 45-day free trial. You can [click here](https://hq.tines.io/pages/c88e1a237bed1f2e43fcf39d597ca6cd/) to opt-in automatically or talk to a member of our team to learn more.

### With cases, you can:

-   Organize your team around a case
    
-   Collaborate in real time on the steps to triage and remediate
    
-   Take actions based on the information at hand
    
-   Automate where you can with other workflows or systems
    
-   Report on what happened and share the outcomes with leadership
    

Cases are best used in tandem with [records](https://explained.tines.com/en/articles/7971187-introduction-to-records), a way to structure event data from across story runs. Records are often attached to cases as artifacts. You can then visualize data from records and cases in a single view with [dashboards](https://explained.tines.com/en/articles/9521649-introduction-to-dashboards). These views help you monitor what's happening across workflows within your team.

> **👉 To learn more about configuring cases, [see our docs](https://www.tines.com/docs/records-cases/cases).**

[Watch video](https://fast.wistia.net/embed/iframe/z5u7ty1dze)

### Key features of cases

The table below outlines the different features of cases.

| **Feature** | **Description** |
| --- | --- |
| **Correlation** | Easily search and correlate in cases in the UI and via API by record data, case metadata, tags, and more. |
| **Automation** | Capture granular changes made to cases with case webhook notifications, allowing you to build custom responses. |
| **Remediation** | Take actions from cases via case buttons, which link to any of your existing Tines automations via webhook or page. |
| **Customization** | Customize key processes to your team by defining your own case statuses, tags, closure requirements, SLAs, and custom roles. |
| **Reporting** | Export individual case data and reporting dashboards for external use. |

## Introduction to dashboards

Gain a singular view across all [cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases) and [records](https://explained.tines.com/en/articles/7971187-introduction-to-records) with Dashboards.

Dashboards allows you to monitor and report on workflow performance, bringing forward the data that you care most about for reporting. Your records and cases tell a story, highlight them with charts, lists, number blocks and notes. You can create or copy over existing record charts to build visual cross story reports, while cases can be listed out in customized views. Organization is key to a strong security posture, and bringing as much detail and context together only strengthens it.

And when you need to share that performance data with leaders and stakeholders, you can export either the entire dashboard or an individual element as an image.

## Types of dashboard elements

There are three main components to building out a dashboard:

| **Element** | **Description** |
| --- | --- |
| Record charts | Visualize records with applied filters in multiple chart types for an aggregate view |
| Case views | Filtered view of cases or case summary data (i.e. MTTR, MTTA) |
| Notes | Context to provide alongside the charts or lists with Markdown support |

Learn more about building dashboards in our [docs](https://www.tines.com/docs/records-cases/dashboards).

[Watch video](https://fast.wistia.net/embed/iframe/ty6pqs9gws)

## Introduction to OpenTelemetry

## What is OpenTelemetry (OTEL)?

OTEL is the open standard for generating, collecting, and exporting telemetry data. In Tines, our telemetry data consists of traces and spans. A trace is a single end-to-end operation executing through the system (for example, a story execution or background job run). A trace is composed of multiple spans or single steps like HTTP call, DB query, background job or action execution.

With self-hosted release [v34.7.0](https://www.tines.com/docs/self-hosting/release-notes/#october-8th-2025-34-7-0), Tines has the ability to send OpenTelemetry traces to observability stacks that support OTEL ingestion.

Read the following documentation on [how to configure this new feature](https://www.tines.com/docs/self-hosting/additional-configuration/exporting-opentelemetry-traces/).

## What are we trying to solve with OpenTelemetry?

When something in Tines feels slow, fails intermittently, or just acts differently under load, the default tools may not always have enough information to act on. You can look at system metrics, see that stories are running, and dig through logs. Even after doing all of that, the same questions remain:

-   What exactly is slow?
    
-   Where is the time actually going?
    
-   Is it this story or action acting up?
    

## Why is OpenTelemetry important?

This is where OpenTelemetry helps. It lets us see how a workflow moves through Tines, rather than just whether it finished. With tracing enabled, you can watch:

-   How requests and background jobs move through the system
    
-   Which parts of a run are really driving latency
    
-   How the different spans fit together inside a story
    
-   What changes when the system is under heavy load or stories back up
    

That changes conversations from:

*“The system feels slow.”*

to something more useful, like:

*“Queue latency is zero, but a couple of actions in Story 42 are slow. We should fix the story, not the infrastructure.”*

Read the following articles for help with:

-   [OpenTelemetry: Designing and Implementing an Observability Stack](https://explained.tines.com/en/articles/14120804-opentelemetry-designing-and-implementing-an-observability-stack)
    
-   [OpenTelemetry: Designing a Dashboard](https://explained.tines.com/en/articles/14120923-opentelemetry-designing-a-dashboard)
    
-   [OpenTelemetry: Practical Troubleshooting](https://explained.tines.com/en/articles/14121076-opentelemetry-practical-troubleshooting-walkthrough)

## Introduction to records

## Introduction to records

Define and structure event data across stories with records. Captured from the storyboard, each row of a record represents a story run.

#### With records you can:

-   Create a system of record
    
-   Store and report on data produced by Tines
    
-   Structure relevant story run and event data
    
-   View data visually through multiple chart options
    
-   Enable the storage of raw logs or extensive data dumps directly within the records table
    
-   Create an “alert” to be further processed or referenced later when [creating a case](https://tines.com/docs/records-cases/cases)
    
-   Define a “retention period” or “time to live” (TTL) for a record type
    
-   Manage parent-child relationships in records through the API
    
-   Create cross story reports using records with dashboards
    

### Record elements

Records are made up of elements that can relate to the overall record or be story-specific. When making changes to record-specific elements, it impacts all stories referencing the record.

| **Elements** | **Definition** | **Story-specific** | **Record-specific** |
| --- | --- | --- | --- |
| **Records** | The data captured from a specific story run; appears as an individual row on the records table | Yes | Yes |
| **Capture record** | Captures data from the story run to the records table | Yes | No |
| **Record type** | The predefined fields across stories within a team | No | Yes |
| **Record fields** | The individual predefined fields within the record type that appear as columns in the records table | No | Yes |
| **Record artifacts** | A large text record field type that enables the storage of raw logs or extensive data dumps directly within the records table | No | Yes |
| **Record inputs** | Determines what incoming event data gets captured in the record | Yes | No |
| **Records table** | The summary of all collected records across stories | No | Yes |
| **Records charts** | Visualize records with applied filters in multiple chart types for an aggregate view | No | Yes |
| **Parent record** | The source data capture for parent-child relationships. If a parent record is added to a story, all child records will also be added. | No | Yes |
| **Child record** | The downstream records associated with a parent record. | No | Yes |

> **👉 Learn more about configuring records in our docs.**

[Watch video](https://fast.wistia.net/embed/iframe/2c4gayd840)

## Introduction to the storyboard

The storyboard is the heart of the Tines platform, a flexible, interactive workspace where automation workflows come to life. The storyboard is an interactive, drag-and-drop builder where you create and visualize automation workflows called "stories."

A story is a collection of interconnected actions working toward a singular mission, like responding to a phishing email, onboarding a new employee, or investigating a security alert.

On the storyboard, you can:

-   Drag and drop actions from the left panel onto the canvas
    
-   Connect actions visually by drawing lines between them to define data flow
    
-   Configure action settings using the properties panel on the right
    
-   See your workflow in action with real-time event badges showing execution status
    
-   Organize and layout your automation for clarity and maintainability
    
-   Collaborate with teammates who can view and edit alongside you in rea**l-time**
    

* * *

## **Key components of the storyboard**

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332065593/14ba6e02b2bfc70e15b647e10210/Storyboard+UI.png?expires=1784786400&signature=03ddd48c67b10b0929e7809c7805492528b164c7f1a3e2672d008fd3beb3c70a&req=diMkFMl4mIRWWvMW3nq%2BgZqKEceZybmN%2BuqxYmDmQJMa32wn696VOGW4%2F02o%0AifN3%2BVPkNpXiIk%2Bewza2FBirYIU%3D%0A)

### **The story toolbar**

The story toolbar is a floating panel on your storyboard that gives you access to all actions, tools, and templates. It's designed to help you build faster, with actions grouped by function so you can find what you need at a glance.

By default, the toolbar surfaces the most commonly used actions. You can personalize it by dragging actions onto the toolbar to add them, or into the Quick Search menu to remove them. Your layout is saved per user, per tenant.

#### **Action types**

-   Webhook - Receives data from external systems to trigger workflows
    
-   HTTP Request - Sends or receives data from any API
    
-   Receive Email - Initiates workflows from incoming emails
    
-   Send Email - Sends email notifications or reports
    
-   AI Agent - Enables AI-powered reasoning and execution
    
-   Event Transform - Manipulates and structures data
    
-   Condition - Makes decisions based on conditions
    
-   Send to Story - Reuses workflows by nesting them within other stories
    

> **👉 Learn more about actions [here](https://explained.tines.com/en/articles/6872936-what-are-actions-in-tines).**

#### **Templates**

Templates live within the HTTP request action type. Search and drag pre-configured templates for popular tools and services. These templates come with pre-filled configurations for common tasks, dramatically speeding up your build time. If a tool offers an API, Tines can connect to it.

> Read about the story toolbar [here](https://explained.tines.com/en/articles/14781675-story-toolbar).

### **The storyboard**

The central area is your workspace where you:

-   Place actions by dragging them from the left panel
    
-   Connect actions by drawing lines from one action to another
    
-   Move and arrange actions to create a clear, logical flow
    
-   Select actions to view and edit their configurations
    
-   See real-time indicators showing which actions have executed and how many events they've processed
    

### **The properties panel**

When you select an action on the storyboard, the right panel displays:

-   Name and description fields for documenting your action
    
-   Configuration options specific to that action type
    
-   An editable options block where you can define the action's behavior using formulas and data from previous actions
    
-   Rules and conditions that determine when the action should run
    

### The event viewer

The event viewer gives you real-time visibility into the data flowing through your story. When you select an action and open the event viewer, you can:

-   View the full output of each event emitted by an action.
    
-   Inspect the structured data produced at each step of your workflow.
    
-   Re-emit or delete events directly from the viewer.
    
-   Use double-click on any key to copy its data path for use in downstream actions
    

> Tip: Use CMD/Ctrl + E to quickly open and close the event viewer at any time.

* * *

## Working on the storyboard

##### **Organizing your storyboard**

When building a story in Tines, things can get messy. Actions overlap, or get crowded, and the overall flow can become harder to follow. Tines gives you two simple tools to quickly bring order back to your board: **Align** and **Tidy**. With these options, you can straighten out actions, space them evenly, and make your storyboard that bit easier to read and manage.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806553537/1f656338288a1dec12955cef3a44/Screenshot%2B2024-10-03%2Bat%2B15_02_50.png?expires=1784786400&signature=beb4c9fcce6ade9fd281289f086311fda25924d1e7f655797576f2478472b4d6&req=dSgnEMx7noRcXvMW3nq%2BgeRsnQ2%2BHxQ%2FXQ18Pw7PGRSn76PARR%2Byql17kFDl%0ARWjV4LkolLMR0ue6rhALROIZZZk%3D%0A)

**Align**: This arranges the selected actions so that they line up neatly, either vertically or horizontally. It’s useful when you want multiple actions to share the same axis.

  
​**Tidy**: This automatically spaces out the selected actions evenly, reducing overlaps and improving readability. It’s helpful for cleaning up a messy storyboard quickly without manually dragging actions around.

You can use **Align** when precision is needed (e.g., keeping several actions perfectly level) and **Tidy** when you want the overall layout to be more organised and visually clearer.

##### **Notes:**

Add notes to your storyboard to document complex logic, explain API requirements, or provide context for teammates. Notes are visible to everyone viewing the story and are particularly useful for:

-   Documenting intricate workflow sections
    
-   Explaining unclear API requests
    
-   Listing required parameters for Send to Story actions
    
-   Linking to external documentation for audit purposes
    

You can add toggle headings (H1, H2, and H3) to your notes to collapse and expand sections, keeping your notes organized without the clutter. Type `/` in any note to add a toggle heading.

##### **Connections:**

Connections on the storyboard define how data flows between actions. When you connect Action A to Action B, you're telling Tines that when Action A completes and emits an event, Action B should receive that event and execute. To connect two actions:

1.  Click on the source action (the one that will send data)
    
2.  Drag from the action to the receiver action
    
3.  Release to create the connection
    

You'll see a line connecting the two actions, showing the data flow path.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2179793903/9b1a5f788aa038344d27647296de/CleanShot+2026-03-19+at+15_25_10.gif?expires=1784786400&signature=b873447035a75fd9dcd5fc28b120a4bf5aa224f8afe288c4ce50ea580de54308&req=diEgH853nohfWvMW3nq%2BgT28EQN%2F5LlqKtKxev6c%2F%2BSLRk1txMRQhbc6Q4FQ%0AtHylQR%2F%2F8aY0iRvCvS3yDmCj7PA%3D%0A)

##### **Understanding event flow:**

An event is the output of an executed action. When an action runs successfully, it emits an event containing the data it produced. This event then flows to all connected actions, triggering them to execute with that data.

The storyboard shows event counts on each action with small badges, helping you understand how many times each action has executed and troubleshoot any issues.

> **👉 Learn more about events [here](https://explained.tines.com/en/articles/6877834-what-are-events-in-tines).**

* * *

### **Best practices**

**Start simple, then expand**

Begin with a basic workflow that accomplishes your core objective. Once it's working, you can add error handling, notifications, and additional integrations. The storyboard makes it easy to add new actions without disrupting your existing flow.

**Use descriptive names**

Give your actions clear, descriptive names that explain what they do. Instead of "HTTP Request 1," use "Fetch user details from Okta." This makes your storyboard self-documenting and easier for teammates to understand.

**Test as you build**

Don't wait until your entire workflow is complete to test it. Use the test functionality on individual actions as you add them to catch issues early.

**Leverage Send to Story for reusability**

If you find yourself building the same sequence of actions in multiple workflows, create a separate story for that sequence and use Send to Story actions to call it. This keeps your storyboards cleaner and makes updates easier—change the sub-story once, and all stories using it benefit.

**Document complex logic**

Use notes on your storyboard to explain any complex decision logic, unusual API requirements, or business rules. Your future self (and your teammates) will thank you.

**Keep it visual**

Arrange your actions in a logical flow from left to right or top to bottom. Avoid crossing connection lines when possible, as this can make the workflow harder to follow.

## Introduction to Tines University

[Tines University](https://www.tines.com/university/) is a free, self-paced learning platform available to anyone. It offers structured learning paths that help you and your team learn how to use Tines, from foundational concepts through to advanced and specialist topics.

### What you'll find in Tines University

University is organized into **learning paths**, each made up of several modules. Every path is built around a specific skill level or topic area, so you can start at the level that's right for you.

Each learning path includes:

-   An estimated time commitment
    
-   Modular content you can work through in one sitting or spread across multiple sessions
    

When you complete a learning path and pass the exam, you earn a badge.

### Available learning paths

#### Starting paths

**Tines Foundations**

Your starting point for understanding Tines. This path covers what intelligent workflows are, how data moves between systems, and how to navigate the Tines platform. Topics include the Tines UI, stories, actions, JSON, APIs, and events.

**Builder: Core**

Learn the building blocks for creating well-structured workflows. This path covers story design, resources, credentials, and an introduction to functions. Best suited for learners who are ready to start building.

#### Continuing builder paths

**Builder: Intermediate**

Build on core concepts with interactive elements and more complex data handling. Topics include pages, explode and implode, intermediate functions, and pagination.

**Builder: Expert**

Focus on enterprise-scale workflow design, including modular and reusable solutions. Topics include Send to Story, change control, loops and tags, and advanced functions.

#### Specialty paths

**Platform Administrator**

Learn how to manage users, configure security settings, and govern your Tines tenant. Topics include authentication, compliance, reporting, and tenant health.

**Builder: AI**

Explore how to integrate AI capabilities into your workflows. Topics include auto generate, automatic mode, the AI agent action, MCP server, and Workbench for Storyboard (formerly Story copilot).

**Specialist: Cases**

Learn how to design, build, and manage cases in Tines for tracking and closing work. Topics include case design, navigation, management, and how cases relate to records and dashboards.

**Specialist: Records**

Learn how to capture structured data from your workflows for reporting, analysis, and automation. Topics include record design, navigation, management, and how records relate to cases and dashboards.

**Specialist: Workbench**

Learn how to use Workbench, Tines' AI-powered chat interface, to take action and access data across your stack from a single conversational view. Topics include Workbench tools, conversations, management, and the integration with Slack.

### Getting started

Tines University is free and available to anyone. You can access it anytime at [Tines University](https://www.tines.com/university/).

If you're not sure where to start, **Tines Foundations** is the recommended starting point if you're new to Tines. From there, you can move into **Builder: Core** and continue based on your role and what you need to learn.

Have questions about University? Reach out to us at [university@tines.io](mailto:university@tines.io).

* * *

When you're ready, you can also sign up for a **Tines Certification**. Just visit our [registration page](https://www.tines.com/get-certified) where you can select either the Core or Advanced certification.

## Accessing your certification

To access your Tines Certification dashboard, log in here: [Tines Certification Login](https://certification.tines.com/login)

Once you have built and tested each exam story, you can submit it within the certification itself, under the relevant **Lab submission** module.

> The Tines Training Team generally delivers grading responses within 72 hours.

## Introduction to Webhooks

## What is a webhook?

A webhook is a way for external systems and applications to send data directly into your Tines workflows. Think of it as a doorway that allows other services to knock and deliver information to your automation in real-time.

When you create a webhook action in Tines, you get a unique URL that external systems can send HTTP requests to. Each time a request arrives at that URL, Tines captures the data and creates an event that flows through your story, triggering your automation.

## Why use webhooks?

Webhooks enable event-driven automation. Your workflows react instantly when something happens in another system, rather than constantly checking for updates.

Common use cases include:

-   Receiving alerts from monitoring tools (PagerDuty, Datadog, Splunk)
    
-   Processing events from collaboration platforms (Slack, Microsoft Teams)
    
-   Handling notifications from code repositories (GitHub, GitLab)
    
-   Capturing form submissions from external websites
    
-   Integrating with third-party APIs that push data to you
    
-   Building custom integrations for any service that can make HTTP requests
    

## How webhooks work in Tines

Here's the typical flow:

1.  **External system sends a request**: Another service makes an HTTP POST, GET, PUT, or other request to your webhook URL
    
2.  **Tines receives the data**: The webhook action captures the request body, headers, and parameters
    
3.  **Event is created**: Tines converts the incoming data into an event
    
4.  **Workflow continues**: Downstream actions in your story process the event data
    

## Webhook vs. page: Which should you use?

It's important to understand the difference between webhooks and pages in Tines:

| **Webhook** | **Page** |
| --- | --- |
| Machine-to-machine communication | Human-to-machine interaction |
| Receives API calls from external systems | Displays interactive web pages for users |
| No visual interface, just an API endpoint | Full UI with forms, buttons, and content |
| For automated integrations | For users to visit, view, and submit data |

-   **Use a webhook when**: External systems or APIs need to send data to Tines programmatically.
    
-   **Use a page when**: Humans need to visit a URL in their browser to see information or submit data (booking systems, forms, dashboards, portals).
    

## Common webhook patterns

Here are a few examples of how you can use webhooks within your stories:

-   **Alert processing**
    
    -   External monitoring tools send alerts to your webhook, which then enriches the data, creates tickets, and notifies teams.
        

-   **Event forwarding**
    
    -   Your webhook receives events from one system, transforms them, and forwards them to multiple downstream services.
        

-   **API gateway**
    
    -   Multiple external systems send data to different webhooks, which normalize and route the data through a unified workflow.
        

-   **Callback handler**
    
    -   After initiating a long-running process in an external API, that API calls your webhook when the process completes.
        

Read more on webhooks in our docs, [here](https://www.tines.com/docs/actions/types/webhook/).

## Introduction to Workbench

### Workbench Demo

[Watch video](https://www.youtube.com/embed/ocC4ouXDLsM?rel=0)

### **What is Workbench?**

Workbench is a Tines-powered AI chat interface that allows you to take action and interact with your proprietary data in real-time. This is an add-on product within the Tines platform, and like all AI in Tines, it is [private and secure by design](https://explained.tines.com/en/articles/9369078-security-privacy-for-ai-in-tines).

This is the one place in Tines that is user-scoped. Meaning, outside of audit logs, it is private to you. From Workbench, you can only interact with stories or action templates that you have permissions to interact with through your team or tenant-level settings.

Workbench is available to all Tines cloud tenants, both paid and community editions. To learn more about its availability, [check out this article](https://explained.tines.com/en/articles/9855931-how-to-access-workbench).

[Watch video](https://fast.wistia.net/embed/iframe/f951y3vffb)

### **Why should you use Workbench?**

Workbench removes the need to switch context between applications. It offers builders a singular view to run and monitor in the context of real-time scenarios or events.

You can perform tasks like:

-   Send a message in Slack
    
-   Look up an employee in BambooHR
    
-   Create a ticket in Jira
    
-   Get detections in CrowdStrike Falcon
    
-   Lock down a device in Jamf
    

By getting connected and responding faster, Workbench helps teams increase operational efficiency, improve the interoperability of your tech stack, mitigate risk faster, and strengthen compliance.

> **Note:** To access Workbench, you need to enable AI-powered features from your admin settings. [Learn how here](https://explained.tines.com/en/articles/9396326-how-do-i-turn-on-or-off-the-ai-powered-features).

### **Get started with Workbench**

To start using Workbench, here are some best practices.

1.  Ensure you have enabled a few stories for Workbench
    

![](https://downloads.intercomcdn.com/i/o/1175828066/2c2dd4074e2f5e722fdb1d17/Enable+Workbench+stories.gif?expires=1784851200&signature=6e784e1c12ee3aa08b3585785f7a20ff03ca1f916c0fd4921a49c5e8f81c1ee0&req=dSEgE8F8lYFZX%2FMW3nq%2BgX9dWg2KcJhLJ%2BKhSrmetB1MU70d5kRcSZbXLP26%0ADCWunn6YklcVnZ53sc6El%2F0ZTY8%3D%0A)

2\. Then start a conversation

It’s that simple. It’s ready to use out of the box without stories enabled, but they make the experience even more powerful. Try it out!

* * *

## Resources

Here are some resources to get started with Workbench:

-   [Docs](http://tines.com/docs/workbench/) - Read up on the technical ins and outs of Workbench
    
-   [Library](https://www.tines.com/library/) - Access stories created specifically for Workbench by the Tines Labs team
    
-   [University](https://www.tines.com/university/) - Users can access this content whether or not they’ve completed their Tines certifications
    
-   [Workbench demo](https://explained.tines.com/en/articles/9859395-tines-workbench-demo-recording) - Watch a full walkthrough of Workbench
    
-   [How to access Workbench](https://explained.tines.com/en/articles/9855931-how-to-access-workbench) - Understand the various tiers of using Workbench
    
-   [Workbench FAQ](https://explained.tines.com/en/articles/9855941-frequently-asked-questions-workbench) - All your burning questions about Workbench answered
    
-   [Using stories with Workbench](https://explained.tines.com/en/articles/9855926-using-stories-with-workbench) - How to make the most of your stories and Workbench
    
-   [Writing effective prompts](https://explained.tines.com/en/articles/12097316-writing-effective-prompts) - Guidance for how to communicate well with AI

## Introduction to Workbench for Storyboard

## Overview

[Watch video](https://fast.wistia.net/embed/iframe/ggg88atvdd)

Workbench for Storyboard (formerly Story copilot) is an AI-powered chat interface to build, understand, and optimize flows on the storyboard. You can use Workbench for Storyboard to start from scratch or to work with an existing story. Workbench for Storyboard makes Tines accessible to the whole team, regardless of skillset.

Open the interface by clicking on the Workbench for Storyboard button on the storyboard.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2472983541/f84d2f1dc89ae46c3526d24a7421/wbsb+left+open.gif?expires=1784786400&signature=82df33850e99eec6947d62ea65f9164489e5adf35d6a5e232ecce770d272f29c&req=diQgFMB2noRbWPMW3nq%2BgR0%2FYmkUlaOrqZGGl%2FyoK6%2FHGqto3GAeOh7gjOZS%0A00HeFLnnW0diVeoW86IEmog1lyY%3D%0A)

## Modes

In order to ensure Workbench for Storyboard only does what you want, it has two modes:

-   **Ask** - Ask anything about the story on screen
    
-   **Build** - Build, edit or debug your workflow
    

### Ask Mode

![](https://downloads.intercomcdn.com/i/o/am0g137l/2473000540/3f633f4e6a08ad9296e27043a827/wbsb+left+mode.gif?expires=1784786400&signature=61c91d8b972cee25217502a994dd27643adf8521e56c74a724a6f3cfbbe143b3&req=diQgFcl%2BnYRbWfMW3nq%2Bgda4oOFRpaEXsp1QLxuZeb17X3QxQFtSWPLyxJI%2F%0ATxp9VIKJRyRfY%2BC7q5UvyCaQsbI%3D%0A)

Ask mode won’t build anything for you, but it can help you understand your stories or brainstorm ideas before going into build mode. It can break down what is in your story for you such as the integration points and the key components.

Once you understand your story, you can switch over to build mode if there are changes you want to make to the story.

### Sample use cases:

-   “My story isn’t running the way I want it to. Can you help me figure out what’s wrong?”
    
-   “I didn’t build this story, can you explain it to me?”
    

### Build Mode

In Build mode, you can:

-   Create a story from scratch.
    
-   Make changes to an existing story.
    
-   Figure out why your story isn’t working.
    

### Review changes

As part of Build mode, you will be able to review the changes made to your workflow by Workbench for Storyboard and confirm whether you want to keep the changes or not.

### Sample use cases:

-   “Can you reformat my email actions so they look professional?”
    
-   “We changed from X tool to Y tool. Help me update the credentials.”
    
-   “I’m a new builder and need help getting started. I want to build a flow that XYZs…”
    

### File attachments

You can send images and document attachments directly to Workbench for Storyboard to provide additional context for your requests.

This is useful when you want to:

-   Share a screenshot of an error or workflow diagram to help Workbench for Storyboard understand your issue
    
-   Attach a document (e.g., a specification, runbook, or policy file) to guide how Workbench for Storyboard builds or modifies your story
    

To attach a file, click the attachment icon in the Workbench for Storyboard chat input before sending your message.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2473028807/91d5c566b5c65620795f1c15d8b7/CleanShot+2026-06-12+at+15_58_21.png?expires=1784786400&signature=543510826422995b6aff0378174edf6ebffd53a3e1e0ed5f0960d77e4ece74f5&req=diQgFcl8lYlfXvMW3nq%2BgRtWFzhoepPEvzzXEQRPbdON16kL7H7CmCkqDgrq%0AqWAC7NlbNW6aIv8WYs17t2ACSps%3D%0A)

> For more information on the files you can attach, check our docs out [here](https://www.tines.com/docs/stories/story-copilot/#file-attachments).

## Access

Workbench for Storyboard can be used with the Tines default model or BYOM, and is available on all tenants, cloud or self-hosted.

> **Note:** To use Workbench for Storyboard, you will need to enable AI features in the AI settings. Follow the steps to enabling AI features in Tines [here](https://explained.tines.com/en/articles/12801322-what-is-ai-in-tines#h_556d7957b7).

Once enabled, Workbench for Storyboard will be available to all users in your tenant.

### Role access

Workbench for Storyboard is aligned with your role. You cannot do something with Workbench for Storyboard that you cannot do yourself.

-   Viewer roles have access to Ask mode.
    
-   Admin and Editor roles have access to Ask and Build mode.
    

### Chat history

While Workbench for Storyboard is available to all users in your tenant, conversations are scoped to the individual Tines user.

To access your chat history, click the drop down arrow.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2473026730/4ddd938db32ecfcf97fb38ec980e/CleanShot+2026-06-12+at+15_57_28.png?expires=1784786400&signature=493438440db62238d76eec5943d46f8971ef8ce58d2fb64f2637caec7abf7054&req=diQgFcl8m4ZcWfMW3nq%2BgUeaZL1QPQHMsBEkxPsP5FUuWUIZfyWxNn8KVBBG%0AoDQ6xnfPaIUZ%2F5i5Db09vkYXsfI%3D%0A)

## Limits & reporting

Workbench for Storyboard uses [AI run-time credits](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits) from your tenant's monthly allocation. Credits are deducted per interaction when using the Tines default model. If you are using a custom provider, it will consume tokens from that provider at your own cost."

### Viewing tokens used in Workbench for Storyboard

You can view the tokens used in each individual chat by clicking on the information button within Workbench for Storyboard. This button only appears after you have interacted with Workbench for Storyboard.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2473029751/7eb23bdbf543c21972e71b74b6e3/CleanShot+2026-06-12+at+15_58_54.png?expires=1784786400&signature=3d4ff50d9ed332488ff8effd7d70ecf304c71c214daa5d260048ef31e832fccd&req=diQgFcl8lIZaWPMW3nq%2BgX6qbOut9SQL5bJpmui3uFvU%2FqOnhU8waTuAvqGh%0AoAtIBXtUSGfp6JG59YaWOT2UnU4%3D%0A)

### Viewing tokens used across your tenant

You can view aggregated team level usage in the AI usage section of the reporting page.

Here, you can view usage by both tokens and credits.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2437517070/1b78a55e4f20e505068d902de7a3/CleanShot+2026-05-29+at+11_54_02%402x.png?expires=1784786400&signature=a3c68d39ee0de13686836e203af7175289e95ab3045a9901895f7bf0722fc7e1&req=diQkEcx%2FmoFYWfMW3nq%2BgUxqs%2FqV68SYVg%2B9wznNx9oJ%2FTWdKfkNwR6tUlqE%0A9DIVjfY4RobD7NeebIq2Apd3Szc%3D%0A)

## Choosing an AI model

Workbench for Storyboard will use the "Smart Model" configured for your tenant.

For the 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.

> Read more on multiple AI provider support [here](https://www.tines.com/docs/admin/ai/#multiple-ai-provider-support).
>
> Steps on adding your own AI provider can be found [here](https://explained.tines.com/en/articles/10371885-use-a-preferred-ai-provider-in-tines).

## IPInfo Authentication Guide

IPInfo refers to itself as the most reliable, accurate, and in-depth source of IP address data available anywhere. They process terabytes of data to produce their custom IP geolocation, company, carrier and IP type data sets.

### First, get an IPInfo API token

1.  Login or sign up to [IPinfo.io](http://IPinfo.io)
    
2.  Click on "Token"
    
3.  Click "Copy to Clipboard"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/832714569/8bb95b4a22c338c7e428084b/image.png?expires=1784851200&signature=87014a9b7779df3c6ba9641085514021cb6c8b2c37910f3cb844dd37a0c1ed87&req=fCMlEch6mIdWFb4V1XW4gdNBjDk1nA34n5utBDUIu8BfC148NHipw2LFjJ5E%0AL%2FNPsgwOjoeux95Xl1AXx8jGgg%3D%3D%0A)
    

### Lastly, create an IPinfo credential in Tines

**IPinfo connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "IPinfo" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/ipinfo).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of IPinfo stories in the [story library](https://www.tines.com/library?s=ipinfo&view=all).**

## IPQualityScore Authentication Guide

IPQualityScore (IPQS) provides enterprise-grade fraud prevention, risk analysis, and threat detection. It analyzes IP addresses, phone numbers, email addresses, and URLs or domains to identify sophisticated bad actors and high-risk behavior.

### First, get an IPQualityScore API key

1.  Login to your IPQualityScore account at [https://www.ipqualityscore.com/](https://www.ipqualityscore.com/)
    
2.  Click on "Settings & Account Management" and then select "Account Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848696974/e47b9d3eb31d97604ab5fe92/image.png?expires=1784851200&signature=659dd2d24d3726819ef731f9ef5e9ebc6b841c5f367c1ad9f2d67b4c5d6c85fe&req=fCQvEMB4lIZbFb4V1XW4gfTPurwcfGKbmesR7AGN2j84FnxuCSMmLYmrhpEw%0AggLhEP36Nf%2F5hebWDm9IMA89yA%3D%3D%0A)
    
      
    ​
    
3.  Copy your "API Key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848697357/dfa0f2e9561c186145f82e0d/image.png?expires=1784851200&signature=eacada8f856e08e7d4e1851c742d75955ee365c0dace313044a3f39ee50a8677&req=fCQvEMB5noRYFb4V1XW4gVFKPKwiIadTarUUcM4nQkYBglgr4RzgYLLKeUuP%0AX3adBND2VLJbMOkPERT%2FqzRSog%3D%3D%0A)
    

### Then, create an IPQualityScore credential in Tines

**IPQualityScore connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "IPQualityScore" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the IPQualityScore connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the IPQualityScore credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    4.  **Domains: \***ipqualityscore.com
        
    5.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/ipqualityscore).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of IPQualityScore stories in the [story library](https://www.tines.com/library?s=ipqualityscore&view=all)**

## Is email mode available for self-hosted Tines deployments?

Self-hosted deployments of Tines cannot use email mode.

Self-hosted deployments can use IMAP mode. Users of self-hosted Tines can also configure an SMTP server in the ENV file.

> **👉 For more details on how to configure email for self-hosted Tines [see here](https://www.tines.com/docs/self-hosting/docker-compose/tines-docker-compose-installation-guide#step-10-edit-env-tmpl-file).**

## Is it possible to create a password-protected file within Tines?

You can create a password-protected file using the ZIP function and the optional password parameter.

> **For more information and examples of using ZIP, [see here](https://www.tines.com/docs/formulas/functions/zip)**

## Island Authentication Guide

Island is a cyber startup that provides a web browser for the enterprise to allow work to flow freely while remaining secure. It gives organizations complete control, visibility, and governance over the last mile helping the enterprise with improved performance and helping businesses to minimize their cyber footprint and create a safe space for work to flow, boosting both security and productivity.

## First, get an Island API key

1.  Login to your Island account
    
2.  Click "Settings" and then click "API"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891023485/c0b4dd3f28bc3cb35993b056/image.png?expires=1784851200&signature=cdedf6f90cbdaaf61ef09e3b065f2684d75932810ed5ef02685e0955bb3fac8d&req=fCkmFst9mYlaFb4V1XW4gVjwXKzO%2FvVTZeylxGpAFGCXcyEzJcackS%2FX504y%0A4cdzyBpPjZu8OJTUG2owLf2jgg%3D%3D%0A)
    
      
    ​
    
3.  Click "+ Create"
    
4.  Name the key, choose an appropriate role and click "Generate API Key"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891023814/57ce83ab69f9072866619f99/image.png?expires=1784851200&signature=cda874ea3a98bf7ad5dc8b26df21a36dc4eb4e84b9f20b896a6120b9f818d870&req=fCkmFst9lYBbFb4V1XW4gd%2BiMqRId09qYkxhr5g0A3Y%2FcKWBLhncAkkvqtMD%0AR4Yy8MF2PZGOI6eqdAlGQ1yYDQ%3D%3D%0A)
    
      
    ​
    
5.  Copy your API key to a text editor and click "Close"
    
    ![](https://downloads.intercomcdn.com/i/o/891024262/e7f28b6e2bbeb616e23b9f0a/image.png?expires=1784851200&signature=6d35ae25a5bc3f4bf3b64615022cfbff291280b025ce0f2811c7193d78c47153&req=fCkmFst6n4ddFb4V1XW4gX8FEHUho7xi7yzF3w5UrshlgAgBIr56Uv71J%2FdM%0AGnOttnOhy%2FiE6qnspNupkDySBA%3D%3D%0A)
    

## Lastly, create an Island credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Island credential
    
    1.  **Name:** Island
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Island** **stories in the [story library](https://www.tines.com/library?s=island&view=all)**

## Is Tines the right product for me?

We built Tines because we were frustrated by the lack of flexibility, complexity, and brittleness of other automation platforms. In contrast, Tines is flexible, lightweight, robust, and easy to use.

The world's leading security and operations teams now trust Tines to automate their essential workflows, making them more effective and efficient. With Tines, you can build automation stories to perform almost any manual task, from onboarding employees to escalating security alerts –and everything in between. Tines is no-code, giving your frontline team members automation superpowers.

### **Here’s a quick look at the product**

[Watch video](https://www.youtube.com/embed/VnrSMh53RVg?rel=0)

At the heart of everything we do is the principle that any manual process, whether related to security, HR, operations, or another IT team, can be broken down and automated using **eight action types.**

The eight Action types we use are:

1.  HTTP request
    
2.  Webhook
    
3.  Send email
    
4.  AI
    
5.  Receive email
    
6.  Event transformation
    
7.  Trigger
    
8.  Send-to-story (nested workflows)
    

These power every process, regardless of complexity. And, even if you're new to automation, you can learn how to use them all before lunch.

We’re proud to say that if it has an API, Tines can connect to it. So, from mainstream solutions to niche and internal tools, Tines connects with your *entire* stack. No waiting for custom integrations: just the tools you already use, working together like never before.

Hopefully, this gives you a basic understanding of what Tines is and can do. You might have realized by now the product is extremely powerful, and the possibilities are endless. But if you have any more questions, contact us using the messenger inside the product - we'd be delighted to chat!

## Is Tines the right product for me?

Not sure if Tines is the right product for you? We built Tines because we were frustrated by the lack of flexibility, complexity, and brittleness of other automation platforms. In contrast, Tines is flexible, lightweight, robust, and easy to use.

The world's leading security and operations teams now trust Tines to automate their essential workflows, making them more effective and efficient. Companies across the world are saving thousands of hours by automating their workflows using Tines.

### **Here’s a quick look at the product**

[Watch video](https://www.youtube.com/embed/VnrSMh53RVg?rel=0)

### **What is Tines?**

With Tines, you can build automation stories to perform almost any manual task, from onboarding employees to escalating security alerts–and everything in between. Tines is no-code, giving your frontline team members automation superpowers.

At the heart of everything we do is the principle that any manual process, whether related to security, HR, operations, or another IT team, can be broken down and automated using **eight action types:**

-   HTTP request
    
-   Webhook
    
-   Send email
    
-   AI
    
-   Receive email
    
-   Event transformation
    
-   Condition
    
-   Send-to-story (nested workflows)
    

These power every process, regardless of complexity. And, even if you're new to automation, you can learn how to use them all before lunch.

We're proud to say that if it has an API, Tines can connect to it. So, from mainstream solutions to niche and internal tools, Tines connects with your *entire* stack. No waiting for custom integrations: just the tools you already use, working together like never before.

### **Problems you can solve with Tines**

Below are ***just some*** of the ways in which our customers have turned the power of Tines into tangible business results.

##### **Security teams use Tines to automatically:**

-   [Analyse phishing emails](https://www.tines.com/story-library?s=Phishing+email)
    
-   [Triage security alerts](https://www.tines.com/story-library?s=security+alerts)
    
-   [Analyse files](https://www.tines.com/story-library?s=analyze+file)
    
-   [Add form data to spreadsheets](https://www.tines.com/story-library?s=add+form+data)
    
-   ***[...and many more](https://www.tines.com/story-library/)***
    

##### **IT Ops teams use Tines to automatically:**

-   [Create Jira issues using Slack](https://www.tines.com/story-library/87653/create-jira-issues-via-slack)
    
-   [Create chatbots for communicating within and between remote teams](https://www.tines.com/blog/chatbots-for-security-and-it-teams)
    
-   [Suspend or disable accounts in Google, Okta, Github](https://www.tines.com/story-library/87650/suspend-or-disable-accounts-in-google-workspace-github-and-okta)
    

##### **DevOps teams use Tines to automatically:**

-   [Handle Github notifications and notify Slack](https://www.tines.com/story-library/1136336/handle-github-notifications-and-notify-slack)
    
-   [Reimage an AWS instance](https://www.tines.com/story-library/87635/reimage-aws-instance)
    
-   [Create Terraform infrastructure as code](https://www.tines.com/story-library/89549/create-terraform-infrastructure-as-code)
    

##### **HR teams using Tines to automatically**

-   [Onboard employees & grant specialized access to specific tools](https://www.tines.com/story-library/1120478/onboard-employees-grant-specialized-access-to-specific-tools)
    
-   [Add new employees to Bamboo HR and manage system access](https://www.tines.com/story-library/87651/add-new-employees-to-bamboohr-and-manage-system-access)
    

> If you've got an idea for a Story in Tines, **[we'd love to hear it!](https://www.tines.com/story-library?tag=IT&modal=request-story)**

Hopefully, this gives you a basic understanding of what Tines is and can do. You might have realized by now the product is extremely powerful, and the possibilities are endless. But if you have any more questions, contact us using the messenger inside the product - we'd be delighted to chat!

## JAMF Pro Authentication Guide

JAMF provides enterprise-secure solutions with consumer-simple workflows to manage and secure devices and users.

### First, create a Jamf API role and client

1.  Login to your Jamf tenant, i.e. `https://<tenant>.jamfcloud.com`
    
2.  Click the "Settings" icon  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855833364/717bc87b4c4f10b430218680/image.png?expires=1784851200&signature=acc8973e59b937cec9461e71830373a0719c346b120223007fa2aa2f9c7a99cb&req=fCUiHsp9nodbFb4V1XW4gc1MLNc%2Fcb79ecD%2FjofRJlRtjrN9bBep6W2DrhQb%0AqO6ChtT%2BCutQOdl6LXPffvJjIg%3D%3D%0A)
    
      
    ​
    
3.  Click "API Roles and Clients"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855833732/61db4684236e86a08edfa65d/image.png?expires=1784851200&signature=81cfa07c3c27ce365c86465fac5579f2ab7facaac431324b4350fbc91d7738d2&req=fCUiHsp9moJdFb4V1XW4gZLmMxuyqZdCgGvJ9oAJDpRePp9NJxO42pE8AUEB%0AmKtNKiaZ6FG%2BbOdLttsOKan0Jg%3D%3D%0A)
    
4.  Click the "API Roles" tab and then "+ New"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855834214/969b8519f6a4f02d6e56fedd/image.png?expires=1784851200&signature=56455112e5de07d44268077b494fec9e5b626b2abf695d06b3519a5bd3cff3b4&req=fCUiHsp6n4BbFb4V1XW4gc1Rwm8UOEec7nu5BxlXl%2BqUewT8r2tdcNBHX4Hj%0AheoBApZ8HzT1FHFCmC%2FJ%2BJxbNQ%3D%3D%0A)
    
      
    ​
    
5.  Name the role, choose the privileges for the role and click "Save"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855834776/18d83336ef3e3154b8debac1/image.png?expires=1784851200&signature=631335c252efa7a6f5618de59b7888f9f3ad317f4466b86aca3def15d20faeba&req=fCUiHsp6moZZFb4V1XW4gcE4zP10YzVewSc%2F5z5jFDjvK0pJ%2FiOBFnKXcVHj%0AgiRDRWJN8i%2B0nG22rA1OvbZinA%3D%3D%0A)
    
      
    ​
    
6.  Click the "API Clients" tab and then "+ New"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855835405/64ac9a5c8751c616375348e8/image.png?expires=1784851200&signature=c46ab328434f70944c6018652983ff0f251aaa0e0e30ea0c84791060523c755c&req=fCUiHsp7mYFaFb4V1XW4gWuSdKByVgtnwV6n%2FNwTjnD1393UNj6ZqRV1c8cD%0AsZ1beQGIGU0eCDdAjbQtPRFHUw%3D%3D%0A)
    
      
    ​
    
7.  Name the client, choose the API role you created, click "Enable API client" and then "Save"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855836140/4afe8a0b2c4a8fccff3b84a9/image.png?expires=1784851200&signature=b83b34e4c29a619a14f11111e82a2e0282a0d752895a4a9e88eeae08e7cdb002&req=fCUiHsp4nIVfFb4V1XW4gQpEj7HXjlXXfekRP4xLG3%2FqZ9j5M9eurDNZlik6%0A9buGmrhGQ5W9qc64dc4gMctxIg%3D%3D%0A)
    
      
    ​
    
8.  Click "Generate client secret"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855836396/459b1b8e2501f7abafdf7c86/image.png?expires=1784851200&signature=035d2da9ff8e5c0c734291692c35ee0f430468f1a8fcebc765fd29ab1442b4ab&req=fCUiHsp4nohZFb4V1XW4gSMGT9ssfak8uikgo0%2Fvyw1eEQd7SUTg%2B8JRm4tF%0AXSr3u4BZ8vJpTi2hWI%2BvBVnP4Q%3D%3D%0A)
    
      
    ​
    
9.  Click "Copy client credentials to clipboard" and paste into a text editor, then click "Close"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/855836779/2474cccd58bfcdad36be3a59/image.png?expires=1784851200&signature=4cde2e2b4c483d540a70b1b234de18294a0691148c43bb9e80a02568f91a27ba&req=fCUiHsp4moZWFb4V1XW4gU2P0RwN%2BQvjvfrolfikt0jV%2BoG8zrcnYKGnUGi4%0AuOGWYKNn2sa0Ha4pAvcK3SrPtQ%3D%3D%0A)
    

## Lastly, create a Jamf credential in Tines

**Jamf connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Jamf" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/jamf).**
>
> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Jamf stories in the [story library](https://www.tines.com/library?view=all&s=JAMF).**

## Jira Software Authentication Guide

Connect Tines to Jira Cloud Platform to create powerful automations with the below authentication guide.

### First, get the relevant credentials needed in Jira.

To authenticate Jira for use with Tines, you will need the following:

-   API token associated with an account
    
-   Email of account associated with API token
    
    -   We recommend creating a Tines service account. That way, actions created and run from Tines will be appropriately identified as from Tines.
        
-   Jira domain
    

##### Step by step instructions:

1.  Log in to [https://id.atlassian.com/manage/api-tokens](https://id.atlassian.com/manage/api-tokens) with the account you would like to associate the API token with.
    
2.  Click "Create API token"
    
    ![](https://downloads.intercomcdn.com/i/o/754220317/c9fa70ec76760000ae20a6f1/Screenshot+2023-06-01+at+12.08.17.png?expires=1784851200&signature=9e65a02328665a1a7c22343f753ffc8be58a78f6d8372a28fccafb27964ef9ef&req=cyUjFMt%2BnoBYFb4V1XW4gVWTZTSc%2FHGHV%2BGfkcZeCeEAUtpR2K8wOpCtFLBC%0Awr5I51Ou6lEaeGbdJ7AKw1GP5w%3D%3D%0A)
    
3.  From the dialog that appears, enter a label (e.g. 'Tines') for your token and click Create.
    
    ![](https://downloads.intercomcdn.com/i/o/754220799/01564cfa1b91f1bbeb70fe37/Screenshot+2023-06-01+at+12.08.48.png?expires=1784851200&signature=b1fe29933266e26c36505901a5b875335f96d1f5e1c6a774e023b1f1f5cc8dd9&req=cyUjFMt%2BmohWFb4V1XW4gZU87Ksgn%2FSnX5aKbHZcaG%2Bf2yopurhhIX%2BXOIpi%0A4IVMH2Vpl7PjNQRw0cNkPTPGkA%3D%3D%0A)
    
4.  Copy the API key
    
    ![](https://downloads.intercomcdn.com/i/o/754221290/57e0004defa9941da5b683d8/Screenshot+2023-06-01+at+12.09.34.png?expires=1784851200&signature=f26bd2b66178dde55a01d215f062298e507a8f650af61c6d2714a5a08cb6f5ec&req=cyUjFMt%2Fn4hfFb4V1XW4gYDCeq3aheCYCkE02ZJ%2F%2BLAf336WXRHdRgFfrxj2%0Ai1OKObUnUvK6E5fVEvZ4Sb%2FgJw%3D%3D%0A)
    

### Then, create a Jira credential in Tines

**Jira Software connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Jira Software" and follow the prompts to connect.
    

##### Manual credential creation

This method is not recommended as the Jira Software connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Jira credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
    4.  **Metadata:** Enable metadata and add the below metadata:
        
        ```
        {
          "domain": "your Jira domain",
          "username": "the email address of the account that created your API token"
        }
        ```
        
        This will allow you to reference the credential's metadata via the `INFO` key. This is also used in our templates.
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the credential.
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/jira-software).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Jira stories in the [story library](https://www.tines.com/story-library?page=1&s=Jira).

## Joe Sandbox Authentication Guide

Joe Sandbox is the industry's most advanced automated and deep malware analysis engine. With a unique multi-technology approach and the latest machine learning techniques, Joe Sandbox enables security specialists to analyze files on Windows, macOS, Linux, Android, and iOS operating systems.

### First, get a Joe Sandbox API key

1.  Login to your Joe Sandbox account at [https://www.joesandbox.com](https://www.joesandbox.com)
    
2.  Click on the person icon and select "User Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/849597161/2aec7dd8b63fa6412a11c717/image.png?expires=1784851200&signature=604f0182e791eb590b5f8c1c94b1375f7b16744935f72e7063129ccdab5b5928&req=fCQuE8B5nIdeFb4V1XW4gY%2BFEUZbfMjGxxpSNSv1vwj6vUYqjZcWaYqrSolf%0AR9IEuzMm8r52WKO2yORovKit1g%3D%3D%0A)
    
      
    ​
    
3.  Click on the "API Key" tab and click "Generate API key" if an API key isn't already displayed  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/849597827/ea1f995c37eb826f04246e26/image.png?expires=1784851200&signature=5b0236746442b28f0c367a5c6cccd0c0b074ccbe5dbaf12ecab8a9192f1098c2&req=fCQuE8B5lYNYFb4V1XW4gYns55Uy%2BYbjZ57kujFTGmoBbaRf8a%2F9q2rIT3qa%0AiOaB0KTdUwfwSwQ5z7uLDCPrKQ%3D%3D%0A)
    

### Lastly, create a Joe Sandbox credential in Tines

**Joe Sandbox connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Joe Sandbox" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/joe-sandbox).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Joe Sandbox stories in the [story library](https://www.tines.com/library?s=joesandbox&view=all)**

  
​

## JumpCloud Authentication Guide

JumpCloud is an open directory platform for secure, frictionless access from any device to any resource, anywhere. JumpClouds mission is to Make Work Happen, providing simple, secure access to corporate technology resources from any device, or any location. The JumpCloud Directory Platform gives IT, MSPs, VARs/Distributors, security operations, and DevOps a single, cloud-based solution to control and manage employee identities, their devices, and apply Zero Trust principles.

## First, get a JumpCloud API key

1.  Login to your JumpCloud account
    
2.  Click on the user menu and click "My API Key"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891028470/36ccb61b7e502a47a0321aba/image.png?expires=1784851200&signature=a7d4eadc21259fee4ee8cf2ab424e127433b328c5d1ab378ec15b4daee4caf02&req=fCkmFst2mYZfFb4V1XW4gXQeLIYZJPb7W8I0Y7MVkFhlhASRnvyVJceJT0UJ%0AltOBYc2vZQEpWjxQ%2BwSIGaJgZA%3D%3D%0A)
    
      
    ​
    
3.  Click "Generate New API Key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891030068/c66288b455164c6fbfae2924/image.png?expires=1784851200&signature=a681cbf2b7df202f7ac9be1d73f06fa0551d4342515e4cb27a816649cde2edd4&req=fCkmFsp%2BnYdXFb4V1XW4gbAlYhp6DI6ed1zCjJDJxkITLJyegMS2TBaMk9a1%0A%2FULCR1gP%2F1jhILws3RqNcfoo2g%3D%3D%0A)
    
      
    ​
    
4.  Copy your API key to a text editor  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891030422/bb11ce4686caa84101c4e3ce/image.png?expires=1784851200&signature=87bc398fef0332d6fd3dd712bac713d35e9d923f7f900e8850e4d572212f2d50&req=fCkmFsp%2BmYNdFb4V1XW4gREB%2BGzhiBZ3DQvbG3RsNhZz6%2B0WhxQEcbGdGoBb%0AGjxI6a2fwQla%2BYw0nFxwxhGddg%3D%3D%0A)
    

## Lastly, create a JumpCloud credential in Tines

**JumpCloud connect flow:**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "JumpCloud" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/jumpcloud).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of JumpCloud** **stories in the [story library](https://www.tines.com/library?s=jumpcloud&view=all)**

## JupiterOne Authentication Guide

JupiterOne is a leading cybersecurity company specializing in cyber asset and attack surface management. Customers use the JupiterOne platform to connect the dots between all assets, people, and risks, providing deep context and insight into their expanding technology footprint. JupiterOne helps teams discover assets, map relationships, and triage risks to reduce attack surfaces.

### First, get a JupiterOne API key

1.  Login to your JupiterOne account at [https://login.us.jupiterone.io/](https://login.us.jupiterone.io/)
    
2.  Click on the menu in the top right and select "Account API Tokens"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848710681/2ebe1fcc3394b453e24d927e/image.png?expires=1784851200&signature=232152629aaaf8f03d4e0409002708dda3b85aa04a841635e2df29118a8a01eb&req=fCQvEch%2Bm4leFb4V1XW4gfeL2HUKIEp5OylRLc4o2i4AA9KpBFh793dNmb9d%0AmYDkZWB%2FCwNE7gil%2Ba%2FDuxlk%2FA%3D%3D%0A)
    
      
    ​
    
3.  Click "New Token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848711450/40079d4318c5e389509178bd/image.png?expires=1784851200&signature=94e329aecd600765be237153f2a084956139dab5367ecef2c9765738e2976688&req=fCQvEch%2FmYRfFb4V1XW4gS8vk2Y2hg4c5oDyf9vgxRpzX5QwJs0sESAz2AZT%0ANxAl25vrhyPiOzB3frwev%2BWnGA%3D%3D%0A)
    
      
    ​
    
4.  Name the token, select the number of days it is valid for and choose the privilege level, and then click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848712720/27947ac56bca75f549b2bd16/image.png?expires=1784851200&signature=25fc3df84536517eae71704493aae1b0362bfab9f7688052680a7165e3d9b43a&req=fCQvEch8moNfFb4V1XW4gZ3LRS%2BknI1UHO%2Bebd9BsjUGdCU45eajjTbgcRJl%0AlQAnw%2Bfe%2Fcf0fa2zggdez%2BQRsA%3D%3D%0A)
    
      
    ​
    
5.  Copy your "Token" to a text editor and click "Done"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848713262/825583d8a8f76f15f6904c0c/image.png?expires=1784851200&signature=df3c2e43b2a9e5c18b5c5f139d3768e2d3809659b9abeefa2b8d36fbc78eec22&req=fCQvEch9n4ddFb4V1XW4gWj0A5avLLajAczRf5MbhCpNI0U2XYZnG7fKaRtu%0Aa1zQHfnKrJun2OtxPlotC%2BhRBQ%3D%3D%0A)
    
6.  Click on the menu in the top right and select "Account Management"
    
7.  Copy your "Account Id" to a text editor  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848715916/3990cf53fae441b27d047c72/image.png?expires=1784851200&signature=0802306515803ad9d680a8dd848f62930c83e18b69b8c9aaa59f9acd69a6b1f4&req=fCQvEch7lIBZFb4V1XW4gWA%2BnrXwA71KC0AoOihzXAhXihpBTLoJop83u2H1%0Abw61jL%2Fd9N6k0IVFBjvZ%2Fkgn3w%3D%3D%0A)
    
      
    ​
    

### Lastly, create JupiterOne resources and a credential in Tines

**JupiterOne connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "JupiterOne" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/jupiterone).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of JupiterOne stories in the [story library](https://www.tines.com/library?s=jupiterone&view=all)**

## KnowBe4 Authentication Guide

**KnowBe4** provides Security Awareness Training to help you manage the IT security problems of social engineering, spear phishing and ransomware attacks.

## First, get a KnowBe4 API token

1.  Login to your KnowBe4 account at [https://training.knowbe4.com/ui/dashboard/](https://training.knowbe4.com/ui/dashboard/)
    
2.  Click on the user menu and then click "Account Settings"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/964882601/46974cb19090cd214f6dda7c/image.png?expires=1784851200&signature=293cc77ef34ef7483334ed5da6553f17ff1782e1db01545f612f48d68fdb816e&req=fSYjHsF8m4FeFb4V1XW4gdbsMgUI2vsJyqWkCXEIZR7W51hB2fl4HV24Hshn%0AJBlyHEJp1WwNx7X8cpvzALFgdw%3D%3D%0A)
    
      
    ​
    
3.  Click "Account Integrations", then "API" and then click on the API you need to create a token for  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/964884105/e230698d0f4048797985da56/image.png?expires=1784851200&signature=236d1c8ae552120fb79a8e7ec0588a01c756c49019f947e378aea9e5dc4bc359&req=fSYjHsF6nIFaFb4V1XW4gb0WaoytqNQzBBOUlUdXzymtz8qJKmCgR%2FtocY7W%0ASnIXJmfT9FB%2BetYyPh9zk42Nbw%3D%3D%0A)
    
      
    ​
    
4.  Click "+ Create New API Token"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/964885412/77246839a942972cbd8fd6fc/image.png?expires=1784851200&signature=3cd4d35e1bac67be6e74ec650fc74beaa78e74c858b73000c9b12376a36893bf&req=fSYjHsF7mYBdFb4V1XW4gb8DSKz4Rz1pensaE2jmfw6aPG%2F6KH%2BnOocDWgua%0ACFjDZEuKkwSpir3eChY%2BZbrAsg%3D%3D%0A)
    
      
    ​
    
5.  Name the token and click "Create Token"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/964886204/f5b70a105188401cfa09e47f/image.png?expires=1784851200&signature=70f7d473f9f164e9e15153ff51d9df90c5265804ae04c61009a62dbe19594f58&req=fSYjHsF4n4FbFb4V1XW4gcIiFlUCWQJ4%2F2wR7K80E3kt1bgEeV8obqSEAa%2Fo%0AG10x0GRVSwaN1B7aMQ9dwwwwuw%3D%3D%0A)
    
      
    ​
    
6.  Copy your token  
    ​
    

## Lastly, create a KnowBe4 credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the KnowBe4 credential
    
    1.  **Name:** KnowBe4
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"  
    ​
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of KnowBe4** **stories in the [story library](https://www.tines.com/library?s=knowbe4&view=all)**

## Malware Analytics Authentication Guide

Secure Malware Analytics (formerly Threat Grid) combines advanced sandboxing with threat intelligence into one unified solution to protect organizations from malware. With a robust, context-rich malware knowledge base, you will understand what malware is doing, or attempting to do, how large a threat it poses, and how to defend against it.

## First, get a Malware Analytics API key

1.  Login to your Malware Analytics account
    
2.  Click on the user menu and then click "Account Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929242759/d709a3a4158b093e66a257f7/image.png?expires=1784851200&signature=f9ed7171ce25951c1d97f77a3b9a71eb6d8a50809464c86da2db3ff5885a940e&req=fSIuFM18moRWFb4V1XW4gbzJuUsAhnKhly9oEfFv0R5Wx6TgVU788KwWH4cb%0A5pNshX3T90LKc5O9x%2B6UpgeCKQ%3D%3D%0A)
    
      
    ​
    
3.  Copy your API Key to a text editor
    
    ![](https://downloads.intercomcdn.com/i/o/929243248/c97e26789f3cc3089aa28b75/image.png?expires=1784851200&signature=86a3c04f32cada5eed922fdab5194517e74d0c888bf13486fffa7f57df061ead&req=fSIuFM19n4VXFb4V1XW4gdMJDrXVwNp1682EfC9OeJoxEtex5nCqXw9wBWer%0AbFewhKO27r0bxrvwVwkpccD7AQ%3D%3D%0A)
    

## Lastly, create a Malware Analytics credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Malware Analytics credential
    
    1.  **Name:** Malware Analytics
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Malware Analytics** **stories in the [story library](https://www.tines.com/library?s=malware%20analytics&view=all)**

## Malwarebytes Authentication Guide

Malwarebytes is an anti-malware software for Microsoft Windows, macOS, ChromeOS, Android, and iOS that finds and removes malware. Made by Malwarebytes Corporation, it was first released in January 2006.

## First, create OAuth2 credentials for Nebula

To integrate Nebula with other applications, you will need to create an OAuth client ID and Client secret. You must be a Super Admin to create OAuth2 credentials in Nebula (see [Malwarebytes documentation](https://service.malwarebytes.com/hc/en-us/articles/10594516370835-Create-OAuth2-credentials-for-Nebula)).

1.  Log in to Nebula.
    
2.  In the left navigation pane, click "Integrate".
    
3.  Click the "Add".
    
4.  In the "Create Client" pop-up window, enter an Application name.
    
5.  Check "read, write, and/or execute".
    
6.  Click "Save".
    
7.  In the OAuth Client window, copy your "client ID" and "client secret".
    

> **IMPORTANT: This is the only time your client secret is displayed. Store it in a secure place. If you lose the secret, you need to generate a new secret, and you will be required to update all integrations that used the old secret.**

## Lastly, create a Malwarebytes Credential in Tines  

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "HTTP Request"
    
4.  Input the values for the Malwarebytes credential
    
    1.  **Name:** Malwarebytes
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://api.malwarebytes.com/oauth2/token`
        
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "**Plain code**" section  
        ​
        
        ```
        { 
        "scope": "read write", 
        "grant_type": "client_credentials" 
        }
        ```
        
    7.  **Headers:**
        
        1.  **Header:** Authorization
            
        2.  **Value:** Basic `<<BASE64_ENCODE("yourmwb-cloud-number**:**yoursecret")>>`
            
5.  Click "Save"
    
6.  Click "Run request" and and double-click on the `access_token` key to copy the path
    
7.  **Location of token from response**: Paste the path you copied `<<malwarebytes.body.access_token>>`
    
8.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
9.  Click "Save"
    

Your credential should look like the following:

  
​

![](https://downloads.intercomcdn.com/i/o/931871269/4e3f319e1f63a1e727814ed7/MalwarebytesCredential.png?expires=1784786400&signature=289e05a9da7df4b1b925962055a513465b8aa98039877a1f98f27083f47391a2&req=fSMmHs5%2Fn4dWFb4V1XW4gcij0oO4lMl0zRoivmsb09xQLB0W%2FG1P%2BwUzbGMG%0AVPVDvG3ydodTsQ%2F%2BqVNJZ9DvUA%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/931871447/606cfb902200039f6c8968d1/Payload.png?expires=1784786400&signature=a605ebe084dd2137d0b1857d3a9f87dbd4d299c5a1ef67a1b6e90420a8205598&req=fSMmHs5%2FmYVYFb4V1XW4gX8V5Anfs792P4x8xwXtCJxtfmQmM2SQQDAylgVD%0AMctLPo7SjcdHMWXdSb5HQH%2FLxw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/931871557/fe4ccd43a272e7a9c9ff5658/Location.png?expires=1784786400&signature=d1f793c635325cb7efba1e8b3b878153e840c10fce10ce147f06c31f22745ebd&req=fSMmHs5%2FmIRYFb4V1XW4gYnfC5Z9NVMF7FyJrGpEBnMZ0O4SgakhPsjGAWke%0AdstKuA1KwWZmIcOUMnHE2phDZA%3D%3D%0A)

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Malwarebytes stories in the [story library](https://www.tines.com/story-library?s=malwarebytes).**

## Using the Credential and Resources in an action

The Header configuration for your Malwarebytes credential should be:

```
"Authorization": "Bearer <<CREDENTIAL.malwarebytes>>"
```

Here is an example Malwarebytes action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"41","actionRuntimeVersion":"6","agents":[{"disabled":false,"name":"Working List suspicious activities of the account in MalwareBytes","description":"Fetch suspicious activity records of the account","options":"{\"url\":\"https://api.malwarebytes.com/nebula/v1/events\",\"content_type\":\"application_json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.malwarebytes>>\",\"accountid\":\"<<RESOURCE.Malwarebytesid>>\"}}","position":{"x":945,"y":300},"type":"httpRequest","timeSavedUnit":"seconds","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"31fd8f347d598606b2c44fec5e0c9bf2a30c670030079f25aa68473848431792","createdFromTemplateVersion":1,"originStoryIdentifier":"cloud:8b3d0c1d536d7aef6416b3d0e57a460a:4a72bb77fd9e057d66d88208bf9b39ea"}],"links":[],"diagramNotes":[]}
```

## Managing your stories

In Tines, you have the ability to move or archive stories and then either restore or delete them permanently. This article will walk you through the steps to do this.

Need a refresher on stories? Read [this article](https://explained.tines.com/en/articles/12688464-stories-in-tines) on stories in Tines.

## Moving a story

You can move a story to another team in your tenant in two places:

1.  On the story dashboard by clicking the 3 dot icon on the story you want to move.  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/2233697024/0d5c0775ec4999ae869da1f0bd60/move+story+in+dashboard.gif?expires=1784851200&signature=2a4b0e98924b5f818fc77c1548a3895aaa4cdfd847fc821ceb968b78070da6a0&req=diIkFc93moFdXfMW3nq%2BgcW7ArZSnqqoYbyjH4c%2FVVjGa5X3CWSXb53Qwg3J%0Ax6vsiKal5WayOOVuB%2BwwgYUKAmI%3D%0A)
    
2.  Within your story by clicking the 3 dot icon in the top right corner of the screen.  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/2233685860/281d7ef9fb6cf69b87f8d7a8916b/Move+story.gif?expires=1784851200&signature=9777392b18109029b7a2a4efa0848c4feefca5aa136f26999ff51c08d4138e66&req=diIkFc92mIlZWfMW3nq%2BgZsv5yFAZmlon0L024Lc6ia7Hjp6DuoB9EGXIHT4%0AfGXvDqg0BQKEBKiHtSSo19sa%2Brw%3D%0A)
    

> Tines tip: It can be very useful to have a dedicated folder, like one for testing, where you can work and collaborate with your team.

## Archiving a story

You can archive a story in Tines by clicking on the 3 dot icon on the story you want to archive and select **Archive Story**.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2223121796/3e788b630af7f0d8456ec880c7f6/Archive+story.gif?expires=1784851200&signature=c329b0fef13e2f554b44c6f4f1e72afa62fc61f9f09a4c3f49e902034f69090d&req=diIlFch8nIZWX%2FMW3nq%2BgRjLjYYw%2BFfONMfYoOERPqllBG0HwG6s2EPaZYAc%0AZz1kXmBCLxowI9SPj%2BC4xm8TVZI%3D%0A)

## Restore or delete an archived story

You can find your archived stories by clicking on your team name, then clicking **Archived Stories**.

Archived stories can be restored or permanently deleted individually or in bulk from this folder.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2223122411/363165301c2b083c238a57dc9015/restore+archived+story.gif?expires=1784851200&signature=bdeaf443d44dae08879ad0e261fad950b31086bf23ea2551bef3654d2841012b&req=diIlFch8n4VeWPMW3nq%2BgU6L%2FvKkdLER5BS2cUI1Y80nn3XtGJGkt%2FormgSQ%0AC4paqLolstqlTBGqbLrRwJM8zTI%3D%0A)

> Note: These archived stories are stored indefinitely until they are permanently deleted.

Read more about stories in Tines [here](https://www.tines.com/docs/stories/).

## Managing your subscription

You can manage your Tines plan on your billing page at `[tenant-name].tines.com/settings/billing`. This centralized hub gives you control over your subscription, usage limits, and billing details.

### **Your plan**

Your billing page displays your current plan tier and associated usage limits. Tines offers different plans to suit various needs:

-   **[Community Edition](https://explained.tines.com/en/articles/13653063-getting-started-with-tines)**: Forever free with basic limits
    
-   **Business & Enterprise**: Plans with expanded limits and features
    

> To upgrade to a business or enterprise plan, **[book a call](https://www.tines.com/book-a-demo/)** with our Sales team to learn more.

### **AI run-time credits**

The number of [AI run-time credits](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits) you have used and have remaining can be reviewed on your billing page.

If you exhaust your monthly AI run-time credits before renewal, all plans *except for* Community edition can purchase additional credits to avoid workflow interruptions from the billing page (`<tenant_name>.tines.com/settings/billing`).

Top-up credits are only used after your monthly allocation is exhausted and will roll into the next month.

When you top up, you'll see the dollar amount of credits used, and available. One credit equals $0.01. Hover over the dollar amount to see the credit equivalent.

Learn more [here](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits).

### **Billing history**

The billing page includes a complete record of your invoices and payment history. You can:

-   View past invoices
    
-   Download receipts for accounting
    
-   Track payment methods and renewal dates

## Mastering the fundamentals

## The storyboard at a glance

Before working through the fundamentals, here's a visual overview of the four key sections of the storyboard:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332299487/bb13b035b56b491c4d69ad6f17fe/Storyboard+UI.png?expires=1784786400&signature=aec4cbd54ce71e3c36a0235c0b316f816dc2f8021afdd31e50c4f46c138908f7&req=diMkFMt3lIVXXvMW3nq%2BgTUpGOS%2BxRW%2F%2FfA8tUPtvjulCgue9aTRlzfxq%2FCc%0A2GWTPSZpPGMl5Pbt5dxM5FOPRsU%3D%0A)

-   Story toolbar — Drag actions, tools, and templates from here onto the storyboard
    
-   Storyboard — Your central workspace for building and connecting actions
    
-   Properties panel — Configure each action using the right-hand panel
    
-   Event viewer — Inspect real-time event data output from your actions
    

## Actions

### Testing an action

To test an action in Tines, select the action and click "Test".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273533632/23642cf305bcdcaac551004b14bf/test+an+action.gif?expires=1784786400&signature=98b5d0384a453b66d711bd2a29f8e93ab59be536e69d5e4c23631d3e2abd1129&req=diIgFcx9nodcW%2FMW3nq%2Bgd1d0aKRlH7cMe1qa1hbZKGgynp%2BmirFqaBrd0Gg%0A801A7TDsqt21NupFcy%2BO0uVpjxo%3D%0A)

This will display the results of the test along with a status code of the action

### Copying an action

To copy an action in Tines, click on the action you wish to copy and then either click the copy button or Ctrl/Command ⌘ + C on your keyboard.

Once copied, you can paste the action into your storyboard.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273536684/ef8404a38c5e6fdb15d975b13815/copy+an+action.gif?expires=1784786400&signature=1eba5f89bdfa7d2b32e58a874beeb265bd5315a13fb2cf5e596c10aa76f3a424&req=diIgFcx9m4dXXfMW3nq%2BgYnRRHpo55UXiTEgy%2BXNJpBmaPIH1y6TjWb1tcNE%0A%2B2r9KmlgPNwP4%2BByqJ5mACIycdo%3D%0A)

### Copying multiple actions

To copy multiple actions, press and hold Ctrl/Command ⌘ and use your mouse to select the actions you want to copy.

Then, the same as copying single actions, click the copy button or Ctrl/Command ⌘ + C on your keyboard.

Once copied, you can paste the action into your storyboard.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273538058/24130a95ffcf33e2b676f7b4c6ad/copy+multiple+actions.gif?expires=1784786400&signature=4e91ec0fae413d3a6cefeeae0affdc744657f8338a79c22e2ba58e1ad25e13e9&req=diIgFcx9lYFaUfMW3nq%2BgX3wInQi4ZBoMW77hTYOjWoIBLuwOolPDS0a4HEZ%0AdLStG9Kl8gTJ%2BEh1wB27jcNPWtw%3D%0A)

### Align and tidy actions

When building a story in Tines, things can get messy. Actions overlap, or get crowded, and the overall flow can become harder to follow.

Tines gives you two simple tools to quickly bring order back to your board:

-   Align: This arranges the selected actions so that they line up neatly, either vertically or horizontally. It’s useful when you want multiple actions to share the same axis.
    
-   ​Tidy: This automatically spaces out the selected actions evenly, reducing overlaps and improving readability. It’s helpful for cleaning up a messy storyboard quickly without manually dragging actions around.
    

With these options, you can straighten out actions, space them evenly, and make your storyboard that bit easier to read and manage.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273622913/f6bed0f06f122c756b44dcdad52d/align+and+tidy.gif?expires=1784786400&signature=0d9b97ea767fd76cabee006ca5e3061678626e6adf285c90c767449a6fdd25a3&req=diIgFc98n4heWvMW3nq%2BgcIcp%2Fs0ohmL%2FvUq6w4zRX800f78CernhAtYkHri%0AnD75f%2B1vuzwPZanQmbCS3ABtZKM%3D%0A)

> Tip:
>
> -   Use Align when precision is needed (e.g., keeping several actions perfectly level).
>     
> -   Use Tidy when you want the overall layout to be more organised and visually clearer.

### Delete a connection between actions

To delete a connection, click the line between two actions and press delete.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273641859/291a80db08f1ffee9c18403dfdeb/delete+a+connection.gif?expires=1784786400&signature=a13fbb34de08e78b95dacf29350a9c31bb738bac62058031e2facae98019ef8e&req=diIgFc96nIlaUPMW3nq%2BgXW78ksDwK33m4L2Enqm3ZEMHHT3WxH1iLuPchxY%0Af%2B7fcbULNy72%2B6CINxng7bguDrg%3D%0A)

To undo your delete, press CTRL or CMD Z.

## Add a note

Notes are helpful for adding information to your story.

To add a note, drag and drop the note action on to your storyboard.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2329178935/954cad0c234ceea409c44aaf2787/add+a+note+new+toolbar.gif?expires=1784786400&signature=7f43baf15c15635c82fbb27562edeaf590d0dfafca2572839cce068f5ec18b0b&req=diMlH8h5lYhcXPMW3nq%2BgU9%2BJ2Gt8i2v1%2FMZ9JNMTHhuyLr2eiReX0SEAobS%0AQmWMFfAJeDdEfILXNihNpukRp5U%3D%0A)

> Tip: You can also click into a blank space on your storyboard and type 'n' to instantly add a note.

### Formatting your notes with toggle headings

You can add toggle headings (H1, H2, and H3) to your notes to collapse and expand sections, keeping your notes organized without the clutter. Type `/` in any note to open the formatting menu and select a toggle heading.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2317154471/5fb72d98fc8b29f7fb3016ec2920/toggle+headings.gif?expires=1784786400&signature=583ecbde438d9961a83a9867be97ee8e95525e5236cbee5f14c5f6a30bcccc2d&req=diMmEch7mYVYWPMW3nq%2BgR27NxQ%2B4XUUG1uWHI9KOtiU%2BzEVbWZVW2SGh3Ac%0AocnvQVm%2BVpe79nIwtawd1cN7i5Q%3D%0A)

## Referencing upstream data

If you want reference data from an upstream action in a downstream action, Rosie from the product team explains it in the video below:

[Watch video](https://www.youtube.com/embed/C2sAQY5_Hdk?rel=0)

### **Steps for referencing upstream data:**

1.  Make sure the actions are connected together in the story. They don't need to be connected directly together, but connected via other actions.
    
2.  To reference upstream data, you need to create a pill in the downstream action. There are three ways to create pills:
    
    1.  Hovering over the event data key and clicking the copy symbol, then pasting it into the payload
        
    2.  Double-clicking the event data key to copy the path, then pasting it into the payload
        
    3.  Select the payload with your cursor and a + will appear. Click +, select value, and manually parse the JSON path to the relevant event key
        
3.  If you need to reference multiple data points from upstream as different keys, click the + sign in the payload to add more pills. You can reference a number, boolean, text, and formulas, amongst other data types.
    
4.  Pills are meant for dynamic references, linking actions together for a smooth workflow. If you want to input data directly without referencing anything upstream, you don't need to use pills. You can type numbers, boolean values, or text directly into Tines, which will be automatically referenced.

## Mattermost Authentication Guide

Mattermost is an open source platform that provides secure collaboration for technical and operational teams that work in environments with complex nation-state level security and trust requirements.

Mattermost is built specifically for technical and operational use cases, including software development and engineering workflows, and integrates deeply with a rich ecosystem of third-party developer tools. Mattermost gives companies full control over their data; with self-hosted and private cloud deployment options and access to the source code, developers can contribute directly to a shared, flexible, and extensible platform built just for them.

## First, get a Mattermost personal access token

1.  Login to your Mattermost account at [https://mattermost.com/](https://mattermost.com/)
    
2.  Navigate "System Console" > "Integrations" > "Integration Management" and enable personal access tokens.
    
3.  Navigate "Account Settings" > "Security" > "Personal Access Tokens"
    
    1.  If your account is a non-admin account, a "System Admin" will need to provide permission to create tokens:
        
        1.  Navigate to "System Console" > "User Management" > "Users", identify your account, and select "Manage Roles  
              
            ​
            
            ![](https://downloads.intercomcdn.com/i/o/853998386/94f22fa53896039150f8bd84/image.png?expires=1784851200&signature=dce939d5face091d4f57d46f29cb2daddeaed4d6bb0f9b467dd0413e87510205&req=fCUkH8B2nolZFb4V1XW4gYrGLwRGXZNNo4HxQtRsnGQhW8wBVy5KZnjE%2BMSN%0AltMehtcUa3pPyf%2FMV2a0u5B%2BtA%3D%3D%0A)
            
              
            ​
            
        2.  Select "Allow this account to generate personal access tokens", any additional permissions and click "Save"  
              
            ​
            
            ![](https://downloads.intercomcdn.com/i/o/853999524/e83ddd66b70b85a382d45d6d/image.png?expires=1784851200&signature=e1c15f24c2959867482c5aeb7536b09a27052ba68f6f864867f84dda62d2b242&req=fCUkH8B3mINbFb4V1XW4gcMUzRuGpESfH3odV922LwMQT3Hkfo6RdIHNXq%2B8%0ApmwP2sE4vcXQrUCMV4N2BeRmTw%3D%3D%0A)
            
              
            ​
            
4.  Click "Create New Token"
    
5.  Give the token a name and click "Save"
    
6.  Copy the access token to a text editor
    

## Lastly, create a Mattermost credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Mattermost credential
    
    1.  **Name:** Mattermost
        
    2.  **Description:** Optional
        
    3.  **Value:** Your token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Mattermost** **stories in the [story library](https://www.tines.com/library?s=Mattermost&view=all)**

## MCP server FAQs

## Overview

**What is the Model Context Protocol (MCP)?**

**[Model Context Protocol (MCP)](https://modelcontextprotocol.io/overview)** is an AI standard [introduced by Anthropic](https://www.anthropic.com/news/model-context-protocol) that defines how large language models (LLMs) can interface with external systems in a structured, predictable way.

**What is the MCP server in Tines?**

The **[MCP server](https://www.tines.com/docs/actions/templates/mcp-server/)** (also referred to as the **MCP server template**) is an [action](https://www.tines.com/docs/actions/) template that you can configure within your story, including many of the same configuration options as our [Webhook action](https://www.tines.com/docs/actions/types/webhook/).

> **Note:** The MCP server is not an MCP client, and does not offer the ability to connect Tines to other MCP servers.

**When is it advised to use the MCP server?**

Use the MCP server template to securely make any API or workflow available to other AI tools that act as an MCP client. Some MCP clients you might want to connect with are:

-   Anthropic Claude
    
-   ChatGPT
    
-   Cursor
    
-   GitHub CoPilot
    
-   Windsurf
    

* * *

## Availability

**Do I have to be a Tines customer to use the MCP server?**

The MCP server template is available for all plans, including Community Edition tenants.

**Is the MCP server available for self-hosted tenants?**

The MCP server template is available for self-hosted tenants on August 18th, 2025.

* * *

## Functionality

**What are Tools and Custom Tools for the MCP server?**

-   The following **Tools** can be added to the MCP server action's configuration: [Public Templates](https://www.tines.com/docs/actions/templates/templates/), [Private Templates](https://www.tines.com/docs/actions/templates/private-templates/), and [Send to Story](https://www.tines.com/docs/stories/send-to-story/).
    
-   The **Custom Tool** option is our [action Groups](https://www.tines.com/docs/stories/groups/) feature that you can build into the MCP server action's configuration.
    
-   A brand new action cannot be added directly as a **Tool** to the MCP server action's configuration; it must be pre-built into one of the features mentioned, **Tools** or **Custom Tools**.
    

**How many Tools can I connect to the MCP server?**

You can connect as many Tools as you need per MCP server template. You can also:

-   Utilize Custom Tools, where you can delegate specialized tasks within them. Please note that Custom Tools are not as flexible as a root-level Tool connected to the MCP server, as all of the actions within a Custom Tool must be invoked when used (the Custom Tool provides a singular output from all of the actions within it).
    

**Does the MCP client gain access to the connected Tools' credentials?**

No, the MCP client does not have access to the Tools' credentials that are integrated within the MCP server in Tines. The MCP client is connected to the MCP server via the provided secret or API key, depending on the Access Control mode you select. There are multiple ways to handle the MCP server's access control:

-   Anyone with the path (public)
    
-   Anyone with the secret (passed in via the MCP server action's URL or HTTP Authorization)
    
-   Only team members (requires a [Tines API key](https://www.tines.com/api/authentication/))
    
-   Members of this Tines tenant (requires a [Tines API key](https://www.tines.com/api/authentication/))
    

* * *

## Pricing

**How much does the MCP server cost?**

The MCP server template does not come at an extra charge.

**Does the MCP server use AI credits?**

No, the MCP server template does not consume AI credits.

**Does the MCP server count as a flow towards my tenant limit?**

Yes, using the MCP server template with connected Tools counts as a flow towards your plan. Tenant owners can validate how many flows you have at the tenant level and delegate Team flow allowances via the tenant **Settings** > **Access & security** > **Story allocation** section.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1654723207/fff65618a9b68cd4476629e08207/image.png?expires=1784851200&signature=d2e38bcebdbe1b94ea1356b8b2ac21ad185ecb7823a955dbf849da543f2c2e51&req=dSYiEs58noNfXvMW3nq%2BgdwZQIogcCkfGdOyHztzM5DN7k9bejGGkcGSoEbz%0A3FFBYYDSo5WI75ZojI9IsBePnRk%3D%0A)

* * *

## Resources

**How do I get started with the MCP server?**

Check out our [how-to article](https://explained.tines.com/en/articles/11931662-how-to-set-up-an-mcp-server-in-tines) to learn more!

**Are there MCP server examples available?**

Yes, you can find examples of MCP server builds in [our Library](https://www.tines.com/library/stories/1324549/).

**Where can I find additional resources on the MCP server?**

-   **Tines Blog**: [Building MCP servers on TInes](https://www.tines.com/blog/introducing-mcp-servers/)

## Microsoft Graph Authentication Guide

**Microsoft Graph** provides programmatic access to a wealth of data that can be used to better inform decision-making during threat detection and response. You can use information such as Outlook emails, organizational structure, advanced threat analytics, and more in your security automation program.

### First, create an Microsoft Graph Credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "OAuth 2.0"
    
4.  Copy the "Callback URL"
    

### Next, create an Azure application

1.  Login to your existing [Azure](https://login.microsoftonline.com/) account or [sign up](https://signup.live.com/signup) if you haven't already signed up
    
2.  Next, you'll need to register a new Azure application in [app registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/CreateApplicationBlade/quickStartType~/null/isMSAApp~/false).
    
3.  Under "Redirect URI (optional)", select "Web" and paste your "Callback URL" you copied earlier.  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799292950/350fdfe0b67b4b23bd205e60/image.png?expires=1784851200&signature=eebfdb55aa02ed865ea1e37699193381b442bc5f0fbfebfbc0757aeca81016a5&req=cykuFMB8lIRfFb4V1XW4ga6RnqvARzOXz9EJ0UqAaUCkr%2BIveJShTSuTYpKz%0AM2khxA%2FmGDTWBKvlb2sKUtrCvQ%3D%3D%0A)
    
      
    ​
    
4.  Next, in your application, navigate to Certificates & secrets > Client secrets and click on the "New client secret" button to create a client secret. Copy the secret to use later.
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799218339/46ec0af21a09f76a0b01cb86/image.png?expires=1784851200&signature=e94f3944e8454147ad06f80aef86c4b064ef33d8199c65d06366023769ceb6d1&req=cykuFMh2noJWFb4V1XW4gfJ8EvZNYzh9Oxo7%2BhMaBNWh2cCefoBmP6nRSUG%2F%0ASz%2BiIM2OJdjTK98GtZLEhWdWmA%3D%3D%0A)
    
      
    ​
    
5.  Next, navigate to API permissions and click on "Add a permission" to add the required permission (see [Microsoft Graph permissions](https://learn.microsoft.com/en-us/graph/permissions-reference)).  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799293856/b1ebe62a8e014e52b44d7d4b/image.png?expires=1784851200&signature=6548cb23f62223dc7ad86a326f121412a54ee5a186f68d0ff204ce6438bb06db&req=cykuFMB9lYRZFb4V1XW4gVDp%2FVkD9CXs0OKW%2FF605LzLspQ3l0robcTObwfO%0A2zGd%2FBjDOe7HKjXdQEsk4PGk2w%3D%3D%0A)
    
      
    ​  
    Microsoft Graph exposes two types of permissions: *Delegated* and *Application* (see [Overview of Microsoft Graph permissions](https://learn.microsoft.com/en-us/graph/permissions-overview?tabs=http))
    
    1.  **Delegated permissions** (also called *scopes*) are permissions that allow an application to act on behalf of a signed-in user. However, the application will never be able to access anything the signed-in user couldn't access.
        
        1.  If you select delegated permissions, you'll need to use the grant type "Authorization code" when configuring the OAuth 2.0 credential in Tines.
            
        2.  Always include “offline\_access” permissions. This scope will allow Tines to request fresh access tokens as necessary. Note: this may not appear in the response from Microsoft (see [here](https://learn.microsoft.com/en-us/answers/questions/806413/scope-offline-access-isnt-being-returned-in-the-to) for more information).
            
    2.  **Application permissions** (also called *app roles*) are permissions without a signed-in user present. The application will be able to access any data that the permission is associated with.
        
        1.  If you select application permissions, you'll need to use the grant type "Client credentials" when configuring the OAuth 2.0 credential in Tines.  
            ​  
            ​
            
6.  Next, you may need to click “Grant Consent” as an administrator for some or all permissions, i.e. application permissions and high-privilege delegated permissions (see [Consent](https://learn.microsoft.com/en-us/azure/active-directory/develop/permissions-consent-overview#consent)).  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799294976/be8830320de3d8b9704a7a63/image.png?expires=1784851200&signature=d83e62310f2168a731d26a86eab35f4a8d9ace66c872dd31b2602a56db1d591f&req=cykuFMB6lIZZFb4V1XW4gYENzkNSwWLvZEebIqdq3GxYb1KOpAOeYkWDpMPg%0AfLzPyTrbZz4YKTuPGdAxyj%2Fs1A%3D%3D%0A)
    
      
    ​
    
7.  Next, navigate to Overview and click "Endpoints".  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799298117/959163611bfbfd20bcffe435/image.png?expires=1784851200&signature=c97477ea296fc5552f8dafaeda2f05a24c7e7c8f73e9506e705a26159a7e1e34&req=cykuFMB2nIBYFb4V1XW4gb%2FZgL0v%2FpGL0WHcEbwqPSGpLz17OB16jl7iJyu3%0A4K60UxG91pjpcbQRddswfhXPIQ%3D%3D%0A)
    
      
    ​
    
8.  Copy the endpoints for "OAuth 2.0 authorization endpoint (v2)" and "OAuth 2.0 token endpoint (v2)".  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799298683/d005e6151ded1c120bd8721c/image.png?expires=1784851200&signature=005675524e7265eb0e9215c1683d8f0a043745bd513d7adf9fb617c57abed50c&req=cykuFMB2m4lcFb4V1XW4geuVs%2BBrL62GFovuPYu20KWAyZuq%2BYEYg7aQV6Q1%0AxdHhiw3nAZfMAYtfY%2BRCfEzN6w%3D%3D%0A)
    
      
    ​  
    ​  
    ​
    

### Lastly, finish creating the Microsoft Graph credential in Tines

##### Authorization code grant type

1.  Input the values for the Microsoft Graph credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Client ID:** "Client ID" found in your application (see screenshot below)  
        ​  
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799300831/bdf4ede1a9fb44018da49b46/image.png?expires=1784851200&signature=06295eb3d9f596cd3de75b8d75e21292e74b76ee50fb8dcf88e5e3994382c969&req=cykuFcl%2BlYJeFb4V1XW4gU5eISdvwMfRiXYi62dQk3ae0ajdArHGMls%2FT%2Fof%0Av2qJQXSL306L8BS0LQgaV25jPA%3D%3D%0A)
        
          
        ​
        
    4.  **Client secret:** "Client secret" created earlier
        
    5.  **Scope:** <this should reflect your applications API permissions created earlier, with each scope separated by a space>
        
    6.  **OAuth Provider:** Manual
        
    7.  **Grant type:** Authorization code
        
    8.  **OAuth authorization request URL:** `https://login.microsoftonline.com/<Directory tenant ID>/oauth2/v2.0/authorize` <the OAuth 2.0 authorization endpoint (v2) you copied earlier>
        
    9.  **PKCE challenge method:** None
        
    10.  **OAuth token URL:** `https://login.microsoftonline.com/<Directory tenant ID>/oauth2/v2.0/token` <the OAuth 2.0 token endpoint (v2) you copied earlier>
         
    
    1.  **Domains:** `graph.microsoft.com`
        
    2.  **Access:** What other teams can also use the API
        
2.  When you click "Save", you will asked to pick an Microsoft account to authorize.  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/799307641/0a430381723defe98c2a2fa0/image.png?expires=1784851200&signature=f59c13b32e255320b9cba6656015aa1893d31bdc07a2e271a497548b19331d07&req=cykuFcl5m4VeFb4V1XW4gSvE%2FAK0VAzz0YRAZqVI%2FdveksEsuMMu5dwjeMO5%0AfX36rl1Oqk7rMYnyoz%2BMs2HuIQ%3D%3D%0A)
    

##### Client credentials grant type

1.  Input the values for the Microsoft Graph credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Client ID:** "Client ID" found in your application (see screenshot below)  
        ​  
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/799300831/bdf4ede1a9fb44018da49b46/image.png?expires=1784851200&signature=06295eb3d9f596cd3de75b8d75e21292e74b76ee50fb8dcf88e5e3994382c969&req=cykuFcl%2BlYJeFb4V1XW4gU5eISdvwMfRiXYi62dQk3ae0ajdArHGMls%2FT%2Fof%0Av2qJQXSL306L8BS0LQgaV25jPA%3D%3D%0A)
        
          
        ​
        
    4.  **Client secret:** "Client secret" created earlier
        
    5.  **Scope:** `https://graph.microsoft.com/.default`
        
    6.  **OAuth Provider:** Manual
        
    7.  **Grant type:** Client credentials
        
    8.  **OAuth token URL:** `https://login.microsoftonline.com/<Directory tenant ID>/oauth2/v2.0/token` <the OAuth 2.0 token endpoint (v2) you copied earlier>
        
    
    1.  **Domains:** `graph.microsoft.com`
        
    2.  **Access:** What other teams can also use the API
        
2.  Click "Save"  
    ​
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Microsoft Graph stories in the [story library](https://www.tines.com/story-library?page=1&s=microsoft)**

## Microsoft Teams Authentication Guide

Microsoft teams allows users to collaborate and communicate in real time within your organization. Connect Tines to Microsoft Teams to create powerful automations that send relevant information to users in Teams.

The below instructions allow users to create a Microsoft Teams application with delegated permissions tied to a user account. For production use, we recommend creating a service account for Tines to link this app to. Depending on your organizational settings, this application may need to be approved by your admin before use. See further details on the application [here](https://www.tines.com/docs/credentials/connect-flows/microsoft-teams/).

Want to create a custom Microsoft Teams credential? Follow [this guide](https://explained.tines.com/en/articles/8210263-microsoft-graph-authentication-guide) for steps on how to create a Microsoft Graph credential manually and enable your preferred custom scopes.

> Read more about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/microsoft-teams).

### **Create a Microsoft Teams Credential in Tines**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Microsoft Teams"
    
4.  Select an account to authenticate with.
    
    1.  **How to select an appropriate account**:
        
        1.  The account selected will be tied to the actions of the application. For example, if you select your account and send a message through Tines, the message will appear to be sent from your account. For this reason, we recommend creating a Tines service account to authenticate with. That way, actions created and run from Tines will be appropriately identified as from Tines.
            
        2.  The account will need licenses relevant to the actions you'd like to automate. For example, if you'd like to send messages in Teams, the account will need a Microsoft365 license to send the messages.
            
        3.  The account will need access to relevant teams you'd like to access. If you want to create an automation in Tines that sends messages in a certain teams channel, the account must have access to that teams channel.
            
    2.  **Admin approval**: depending on the account selected and your organizational settings, you may need to receive admin approval for the Tines application before you can successfully authenticate.
        
5.  Your credential is created and will appear in Tines. Select the credential to view further details about its associated account and scopes.
    

### **Test the Microsoft Teams Credential in Tines**

1.  Navigate to your Tines storyboard.
    
2.  Select 'Templates' and navigate to the templates for 'Microsoft Teams'.
    
3.  Drag and drop the template 'List all teams' onto the storyboard. You should see your created credential appear at the bottom right of the template builder.
    
4.  Select 'Run' on the action template.
    
5.  Select 'Events' to view the results of the action, in this case, to view the list of all teams in your Microsoft Teams organization. Congrats, you have successfully tested your Microsoft Teams credential.
    

### **Send a message with the Microsoft Teams Credential in Tines**

1.  Within your Tines storyboard, drag and drop the template 'Create a one-on-one chat' onto the storyboard.
    
2.  Within the template builder, enter the two user IDs or emails to start a chat between. Please note, one of the users must be the account you have associated your Tines Microsoft Teams credential with.
    
3.  Select 'Run' on the action template. A chat should be created between the two users and the chat ID will be returned in the event data.
    
4.  Drag and drop the template 'Send simple message in a chat' onto the storyboard and connect the first 'Create a one-on-one chat' template to this one.
    
5.  Within the template builder of 'Send simple message in a chat', specify the chat ID that was returned from your action in step 3. You can specify this dynamically by referencing the path to the chat ID or hard-code the chat ID into the template.
    
6.  Select 'Run' on the 'Create a one-on-one chat' template. Tines will now create the chat and then send the message to the created chat.
    

### **Microsoft Teams Application Details**

The application that users are authenticating with is built by Tines and verified by the Microsoft Partner center. Within your Azure environment, the application can be found under the name 'Tines (Microsoft Teams)'.

### **Permissions**

This application has 8 configured delegated permissions necessary for users to complete most common tasks required in Microsoft Teams, such as sending messages. The scope *ChannelMessage.Read.All* is a sensitive scope requiring admin permission.

1.  *Channel.ReadBasic.All*: read the names and descriptions of channels
    
2.  *ChannelMessage.Send*: send channel messages
    
3.  *ChannelMessage.Read.All*: read a channel's messages
    
4.  *Chat.ReadWrite*: read and write user chat messages
    
5.  *Team.ReadBasic.All*: read the names and descriptions of teams
    
6.  *TeamsAppInstallation*.ReadForUser: read user's installed Teams apps
    
7.  *User.Read*: sign in and read user profile (necessary to authenticate)
    
8.  *offline\_access*: maintain access to data you have given it access to (this prevents you having to sign in each time you want the automation to run).
    

### **Frequently Asked Questions**

1.  Do I need to get admin approval for the Tines Microsoft Teams application? *Yes. Admins can approve the Tines app just once for the organization and then you can create as many Microsoft Teams credentials as desired without needing additional consent. For the scope ChannelMessage.Read.All, this is a sensitive scope and requires admin approval.*
    
2.  Does the account I authenticate with for my Tines Microsoft Teams credential need to have admin permissions? *No, the account associated with the credential does not need to have admin permissions. Once the Tines application has been approved by your admin, you can associate the created credential with any account of your choice. We recommend associating with an account that clearly denotes Tines is connected and performing actions to avoid confusion.*
    
3.  What if I want my Microsoft Teams app to have more permissions or functionality than the Tines managed application has? *You can create an application with custom permissions for your organization as well. Follow [this guide](https://explained.tines.com/en/articles/8210263-microsoft-graph-authentication-guide) for steps on how to create a Microsoft Graph credential.*

## Migrating stories from non-production to production tenants

Tines stories can be migrated from non-production tenants to production tenants. This article outlines the procedures to do so, including both manual migration steps and automated change management setup for multi-tenant architectures.

### **Automated migration through a Tines Story**

Tines offers a migration workflow that manages the promotion of stories from a non-production tenant to a production tenant using the Tines API. It is important to note that although most Tines components can be automatically migrated, there are limitations to the API.

This automated approach is ideal for the story migration workflow itself, ensuring consistency and reducing human error. In the following section, we'll walk through the process of setting up an automated story migration system.

#### **Automatic migration**

-   Stories
    
-   Records
    
-   Actions
    
-   Resources\*
    
-   Private Templates
    

#### **Manual migration required**

-   Credentials
    
-   Resources\*
    
-   Case Fields
    
-   Case Settings (statuses, priorities, sub-statuses)
    

> **\* Note:** Resources may contain environment-specific data that differs between non-production and production. For domains, best practice is to use metadata within the credential instead of a resource. If values are the same across environments, nothing needs to be done and migration can be handled automatically.

### **Steps to set up dev to prod tenant migration story**

-   Import this [story](https://www.tines.com/library/stories/1321490/?name=migrate-stories-from-development-to-production-tenants-in-tines&redirected-from=%2Flibrary%2Fstories%2F) from the Tines story library into your non-production tenant.
    
-   Add your `dev_tenant_api_key` to the Tines story. This is the API key for your non-production Tines tenant.
    
    -   You should include the non-production tenant domain as metadata.
        
-   Add your `prod_tenant_api_key` to the Tines story. This is the API key for your production Tines tenant.
    
    -   You should include the prod tenant domain as metadata.
        
-   Optional - add a resource called `prod_tunnel_name`. This is the name of the tunnel used in the production tenant. The story will automatically update any HTTP request actions using a tunnel with the proper production name. Only applicable if tunnels are used and the tunnel naming convention is different between tenants.
    

### **Manual migration required**

While many Tines components can be transferred automatically between tenants using stories, certain elements require manual migration. The following items cannot be pushed to a production tenant through automated workflows and must be configured directly in the target environment.

### **1\. Credentials migration**

#### **Why manual migration is required**

Credentials contain sensitive data that cannot be read via the Tines API and therefore cannot be automatically created or updated within the migration workflow. The workflow does include steps to compare credentials in the non-production and production tenants and will list which need to be created or updated based on the naming.

Best practice is to have the credentials in the production and non-production tenants have the same name with differing environment-specific values (e.g., API endpoints, authentication tokens). Non-production credentials point to non-production systems, while production credentials must point to production systems.

#### **Steps to migrate credentials**

##### **Before migration (Non-Prod)**

-   Navigate to Credentials in the non-production environment
    
-   Document all credential names (exact spelling and capitalization matter)
    
-   Ensure credential names follow a consistent naming convention
    
-   **Important:** Avoid environment-specific names like "tines non prod" - use generic names like "S1" or "ServiceNow"
    

##### **During migration (Prod)**

-   Navigate to Credentials in production
    
-   Create new credentials with identical names to non-production
    
-   Use production-appropriate values (prod API endpoints, prod tokens, etc.)
    
-   Verify each credential is properly configured
    

##### **After migration**

-   When stories are promoted, they will automatically connect to credentials with matching names
    
-   If names don't match, you'll see "reconnect" or “replace” warnings in actions
    
-   Test each credential connection in promoted stories
    

##### **Credential naming best practices**

-   ✅ Best Practice:: S1, ServiceNow, Cribble, Zscaler
    
-   ❌ Avoid: tines non prod, S1 dev, ServiceNow test
    

* * *

### **2\.** Resources migration

#### **Why manual migration might be required**

Resource values may need to differ between environments (ServiceNow state mappings, environment-specific configurations). The workflow will automatically create and update resource values so if resource values need to be different between tenants, DO NOT have them be named the same.

#### **Steps to migrate resources**

##### **Assessment Phase**

-   Navigate to Resources in non-prod
    
-   For each resource, determine if values should be:
    
    -   Identical between environments (queries, mappings that don't change) → Nothing needs to be done.
        
    -   Different between environments (ServiceNow state mappings, environment-specific configurations) → Follow steps below.
        

**Migration Steps**

-   Navigate to Resources in production
    
-   Create new resource with different name from non-production
    
-   Modify values as appropriate
    
-   At the bottom of each resource, configure team sharing if needed:
    
    -   Check which teams should have access
        
    -   Example: Share Security Engineering resources with Security Operations
        
-   Once story is migrated, replace the non-production resource with the production resource
    

### **Resource Sharing**

Resources live within a team but can be shared:

-   Open the resource
    
-   Scroll to bottom
    
-   Check boxes for teams that need access
    
-   Example: Security Engineering can share resources with Security Operations
    

* * *

### **3\. Case fields migration**

### **Why manual migration is required**

Case fields are tenant-level configurations that don't transfer with stories.

#### **Steps to migrate Case fields**

##### **Documentation phase (Non-Prod)**

-   Navigate to Cases → Case Settings → Fields
    
-   Document all fields currently configured:
    
    -   Field name (exact spelling)
        
    -   Field type (text, number, timestamp, boolean, etc.)
        
    -   Any special configurations
        

##### **Migration phase (Prod)**

-   Navigate to Cases → Case Settings → Fields
    
-   Click New Field for each field
    
-   Enter exact field name (must match non-prod)
    
-   Select appropriate field type
    
-   Configure any additional settings
    
-   Repeat for all fields
    

##### **Important notes**

-   Field names must match exactly between environments
    
-   Fields are used across all case types (not application-specific)
    
-   Purple highlighting in cases indicates field mapping from different sources
    

* * *

### **4\. Case settings migration**

#### **Why manual migration is required**

Case settings (statuses, priorities, sub-statuses) are tenant-level configurations.

#### **Steps to migrate case settings**

##### **Statuses**

-   Navigate to Cases → Case Settings → Status in non-prod
    
-   Document all custom statuses beyond default Tines statuses
    
-   In prod, navigate to Cases → Case Settings → Status
    
-   Add each custom status with identical naming
    

##### **Priorities**

-   Navigate to Cases → Case Settings → Priority in non-prod
    
-   Document all custom priorities
    
-   Recreate in prod with identical naming
    

##### **Sub-statuses**

-   Navigate to Cases → Case Settings → Sub-Status in non-prod
    
-   Document all sub-statuses and their parent statuses
    
-   Recreate in prod with identical configuration
    

##### **Tags**

-   Document all case tags used in non-prod
    
-   Create matching tags in prod before promoting stories
    

* * *

### **5\. Best practices for environment-agnostic code**

#### **Use INFO formula (Critical)**

❌ Bad Practice - Hard-Coded Values:

`Status: 2265`

✅ Good Practice - INFO Keyword:

`Status: <<INFO.cases.analysis.id>>`

#### **Why this matters**

-   Hard-coded values (like `2265`) won't work in prod if IDs differ
    
-   INFO keyword dynamically retrieves the correct value for each environment
    
-   Stories become truly portable between environments
    

#### **INFO keyword examples**

For Case Statuses:

`<<INFO.cases.status.draft.id>>`

`<<INFO.cases.status.to_do.id>>`

For Credentials:

`<<INFO.credential.S1.id>>`

`<<INFO.credential.ServiceNow.id>>`

For Domains:

`<<INFO.credential.S1.metadata.domain>>`

For Case Fields:

`<<INFO.cases.inputs.closure_code.id>>`

`<<INFO.cases.inputs.analysis.id>>`

For Records:

`<<INFO.record.record_type_name.id>>`

For Tenant Information:

`<<META.tenant.domain>>`

### **Code review checklist**

Before promoting stories, verify:

-   No hard-coded status IDs
    
-   No hard-coded field IDs
    
-   No hard-coded credential IDs
    
-   No hard-coded domains/endpoints
    
-   All environment-specific values use INFO or META keywords
    
-   Credential names match between environments

## MISP Authentication Guide

MISP Threat Sharing (MISP) is an open source threat intelligence platform. The project develops utilities and documentation for more effective threat intelligence, by sharing indicators of compromise.

## First, get a MISP auth key

1.  Login to your MISP account
    
2.  Click "Administration" and then click "List Auth Keys"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891081099/4bc49ee70c84e7d817700c55/image.png?expires=1784851200&signature=d80c88bc3d70d78b38f724103765f8bdad3a422fb80479c5d2e6d09c6168520b&req=fCkmFsF%2FnYhWFb4V1XW4gaBp6ymBf%2BdnBcwQFGdbH4FbFoCbxx0z7N60jAH4%0A1ixhr0SEQHsaf8HdTxIU0m6F7A%3D%3D%0A)
    
      
    ​
    
3.  Click "+ Add authentication key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891081799/0b9a4617053edb1d1978e65a/image.png?expires=1784851200&signature=abc5346953a54764450a895f0bd98f9dd0939dd7cc70df9b9cef7f8395520b39&req=fCkmFsF%2FmohWFb4V1XW4gW38SpluLEojoTxiJM0xs%2FmG3EnsIH5Ptipq74AW%0ABIbADXQtfB3IkbuQ6ZqdH90UOA%3D%3D%0A)
    
      
    ​
    
4.  Add a descriptive comment, limit access to required IPs, and set an expiration date, then click "Submit"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891083259/112075374adefe1a7a3d3f14/image.png?expires=1784851200&signature=3ed2b243bb59a15d516e4faa56d106c0db8efc7d3ea5855d8d95e21357e74bfe&req=fCkmFsF9n4RWFb4V1XW4gS%2FZQ8K3GvTXELkSDHC6oiUOTBZarZmT%2FnWEC9oc%0ALQiwDV1GRYNxKHXFAqlpJTR2Lg%3D%3D%0A)
    
      
    ​
    
5.  Copy your key to a text editor and click "I have noted down my key, take me back now"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891084056/5d3bdba00c825335b5242190/image.png?expires=1784851200&signature=25b4485362e0863f7a7307da2565a993d5b8d93cdea172feaafb417b7af17990&req=fCkmFsF6nYRZFb4V1XW4gZWwZAnlfaWoJGAVOvdu2R3IucKZO6T0zS9GTYNa%0AeNuK5RUFGc5JSlc85PzADaT3qw%3D%3D%0A)
    
      
    ​
    

## Lastly, create a MISP credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "MISP" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/misp).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of MISP** **stories in the [story library](https://www.tines.com/library?s=misp&view=all)**

## Monitoring actions

> **Note: Monitoring is not available for stories in [TEST](https://www.tines.com/docs/stories/change-control/) mode**

Tines offers three kinds of monitoring for your story. All three kinds of monitoring happen on an action level, so they trigger for the action, not the story. The option can be found in the right-hand builder panel under the status tab:

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/794543538/9939fac5c8188444b4426700/INMDv86ALQkhpvPGfOOOYuYdRZbX5uYrGu79luF-FupCQNlTkYx3nBB0cpxz5_XyMr5ANdsrBoIhaG3--x9EqnTZkXfn6ew29Gnxaq5AIYtUKZhjJVyof_Tla40oqjKV_IECzIY6vrLQd3_u3yCU4U0?expires=1784851200&signature=225ef0a0b42779e8c860b924dccc3d75a802833a1f6c352ba9f26b5ea384420c&req=cykjE819mIJXFb4V1XW4gaEUWbAnLwO0yOVPhRU8vi7PVfSH5dRiB7aqPB4c%0AO98A35ZR5JWNf9LxZCJL85SAug%3D%3D%0A)

-   “No event is emitted within X time”,
    
    -   This function will notify if no events are emitted by the action in the last X minutes/hours/days. The function is usually used to confirm data is still coming into the story by enabling this on incoming webhooks or mail actions.
        
-   “Notify with all events”
    
    -   This will send a notification for all events being emitted by the action. This is usually only used to notify you of actions that you don’t expect to emit events, like error-handling actions built into the story.
        
-   “Notify when action fails”
    
    -   This will send a notification if an action fails. An action failure is anything that would mark the action with a red bubble. This can be due to errors in the functions used in an action.
        
    
    ![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/794543546/2fec20ff5358a0e8ba31ddd1/vRpXAT_9JFroChvflhA3xB7tn0UQ2XbCYxwo8f4c531wc44kP2BxVPbNrgAraWavMSAgWZwVkpf9MCmr0mrguU6OWcTYfISv2SiQZ89PGDYwvYZyD5O689_pO0Ka2BmQbSzFojnb5DeDr9W50ddTgoE?expires=1784851200&signature=2e073ec3a82c879acebcf239bf3a9cc9803efd0a5cc1978328e8e7b6be75e6f0&req=cykjE819mIVZFb4V1XW4gam3kx92pHWofb%2Fj9AF8kmAfIP1MFMMxYRXQTL6e%0A2ARNLaxO2lPaNjxeZCkSCqBJtg%3D%3D%0A)
    

> **Important note:** “Notify when any action fails” option can be configured on the story level, but this function only works by enabling and forcing “Notify when action fails” on all actions in the story.

You can find the story-wide setting on the right-hand panel. It can accept either mail addresses or webhook URLs.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/794543553/9619f4bf4ef86caff38441d7/wL0T6m0YNfVt-ipo3KvWkAusl4ZgSp5XSXd2qaBUaXc9SZDKv-sGyhTZa4MvCZ6zIq_-7h13hCoPfY7KzHm9fZs4hGAPmyXb-3_UjW7OeM1jsQ4zoIZBfzRmZe8vpYJDXqmk2RULH89_MZno7_I2Iaw?expires=1784851200&signature=8878701b9156f1459dbdc221d26230ab636d69c416557d25f07bee667d1b7e05&req=cykjE819mIRcFb4V1XW4gRjxG%2Fp%2BUtZC8vf%2FpEX%2Bdj9v3B2XlbVTVqTC%2FRQj%0A%2FhJYtIP%2BRp%2FMx0hczNlCCuTKvQ%3D%3D%0A)

Regardless of the action or story setting, all notifications will be sent to all addresses or URLs listed in the story settings.

The email may look like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2299229671/c32701e40fe0fd1d8246e35c4da2/CleanShot+2026-04-20+at+16_52_45.png?expires=1784851200&signature=23137539a76f3fe2d1cc379cbb9e09454d18a7f0a965726c2b6bc17ac9939e9f&req=diIuH8t8lIdYWPMW3nq%2BgWUr25FH2DKK4rwu8wi5ERAw475zdqLenik8e4vA%0A7thGNsu86CbeU%2FcsoJiACZf6klE%3D%0A)

## New Relic Authentication Guide

New Relic is a US-based web tracking and analytics company. The company's cloud-based software allows websites and mobile apps to track user interactions and service operators' software and hardware performance.

### First, get a New Relic API key

1.  Login to your New Relic account at [https://login.newrelic.com/login](https://login.newrelic.com/login)
    
2.  Click on your user account and select "API Keys"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850593480/94b4dc58d45ddfe9b709db46/image.png?expires=1784851200&signature=a422ad840e5d2a95ed771f24854c894090c677727887fba037ac43d518a97f02&req=fCUnE8B9mYlfFb4V1XW4gTKU44Tl5QkaHjLpXcNMSoJk%2Bvq9Nk1MsIrqa0Wk%0AtK1E7gO6sW20M2w73mPztoqXKg%3D%3D%0A)
    
3.  Click "Create a key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850596782/3764c21f62e667f759df71e3/image.png?expires=1784851200&signature=a3be53d6f435aab3ad68ceb66717d47061b5db684fde28e1d539a699245747e6&req=fCUnE8B4moldFb4V1XW4gXBOldo9AeL4Bd72YO9z2V9suE8Yg%2FqUhIltMByQ%0Ay%2B4D5LOSZzW4ABj7G8suDdz86g%3D%3D%0A)
    
      
    ​
    
4.  Name the key, select the appropriate "Key type" and click "Create a key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850603356/e4f47c771db1e8b2921c0cf2/image.png?expires=1784851200&signature=ffce7069475a8415e45b8a1374cbbeaaad7e07bdebbdc6e954360d4e2a891364&req=fCUnEMl9noRZFb4V1XW4gdF99rnExliWEJ3tKC4DyKYXPRbD1vyeYPgiAPNS%0AB2322yFz1EtFQPQhM6Hcdwe2gw%3D%3D%0A)
    
5.  Click the ellipsis and select "Copy key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/850671938/4b3b255063b960115fef8135/image.png?expires=1784851200&signature=57374e8864e7c0066c1a6e6319a4563d83fcd46a21ce746f5ae2a5e8bbb8460a&req=fCUnEM5%2FlIJXFb4V1XW4gVNgW0d5nZ1XWoNO%2Fy01SAOlKS1%2BNDR1YBcA%2FbQC%0APCZKrbDw%2BJB1Ga0HB2kmpD9Ezw%3D%3D%0A)
    

### Then, create a New Relic credential in Tines

**New Relic connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "New Relic" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the New Relic connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the New Relic credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    
    1.  **Domains:** api.newrelic.com
        
    2.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/new-relic).**
>
> **For more on creating credentials in Tines click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of New Relic stories in the [story library](https://www.tines.com/library?s=new+relic&view=all)**

## NextDNS Authentication Guide

NextDNS is a personalised DNS nameserver, that can be used to increase the security of your network by blocking malicious domains, block ads and trackers, and limit the browsing experience for your kids.

## First, get a NextDNS API key

1.  Login to your NextDNS account
    
2.  Click on the user menu and click "Account"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891085571/96fa0fad2d7d294e1f45e7b9/image.png?expires=1784851200&signature=29f51d0b517eea195043b5be61814b53a4e881d5a18101c6b5a4113034889b99&req=fCkmFsF7mIZeFb4V1XW4gapYwZm44NIKzU13KjtsysKIKsfOqJ7VwwTYaQPn%0AXhkAt8xzu%2FTZuaxqnFLHxrh39A%3D%3D%0A)
    
      
    ​
    
3.  Copy your API key to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891085961/793666ada9e906392b928d28/image.png?expires=1784851200&signature=96aa2081e88a7cf16ca409e3509d3325964bb2cedb8259874058c273c9715598&req=fCkmFsF7lIdeFb4V1XW4gRHHHh5n7ZlSZCCRPkXpRNn6CqdKAuSICZ%2BybVnS%0A9qKL6SyB6z%2B7PxLo9baskhId7Q%3D%3D%0A)
    

## Lastly, create a NextDNS credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the NextDNS credential
    
    1.  **Name:** NextDNS
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of NextDNS** **stories in the [story library](https://www.tines.com/library?s=nextdns&view=all)**

## Nightfall AI Authentication Guide

Nightfall AI is the leader in cloud data loss prevention (DLP) for generative AI (GenAI) tools, SaaS apps, and custom apps. Download the Nightfall Chrome browser plugin or integrate via APIs to protect PII, PHI, PCI, secrets, and keys across apps like ChatGPT, Slack, GitHub, Confluence, Google Drive, and more.

## First, get a Nightfall AI API key

1.  Login to your Nightfall AI account
    
2.  Click on "Overview"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893651548/f388e9fc51a40cb7bbacb20a/image.png?expires=1784851200&signature=ccda094b46e6e4824f059dc55589706e889c8b34f57694ff56bf848f70ce7e6d&req=fCkkEMx%2FmIVXFb4V1XW4geuomlsxfw0Q02s%2FucoapVrvAPN5yCbotO1AChz3%0A6YO%2Fr0JwscKVfiTRGOXUcvGJBA%3D%3D%0A)
    
      
    ​
    
3.  Click "Create key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893651847/2d4252f9cf2ca989d64ea1a8/image.png?expires=1784851200&signature=4129efd0fa67a5c70766381cc2706912183aab364a152869e08ad358a4bb09c8&req=fCkkEMx%2FlYVYFb4V1XW4gX1DDEh1y1amJfVzGOS%2FJ9PLPHja%2Fx%2BhxBRhrwQU%0AiqWRRAs2xel6d%2FZ1eaCj%2BlnWlw%3D%3D%0A)
    
4.  Name your API key and click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893652153/2525800cf0ed86f0fac5f29d/image.png?expires=1784851200&signature=63599c6b0b61c8eb3320328cb08e912909d41a94a942b04446f6232a5ac44f64&req=fCkkEMx8nIRcFb4V1XW4gRfti%2FUKgl9u6BgZf23CnkBairmOwmsFA0zig2Kz%0AJxuQpb3WQ9M7k87ZhRZlSmsC6w%3D%3D%0A)
    
      
    ​
    
5.  Copy your API key to a text editor and click "Got it"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893652439/bc1f3ae5bff85cf2aacd8f85/image.png?expires=1784851200&signature=a0b21e5ff719186d124d46b748ad5e61fbb590f55b57118c047479b07c28875b&req=fCkkEMx8mYJWFb4V1XW4gejP4ZkK4YgdWXyLyhzXCA%2FGamYxzNqgh9b3ff8%2B%0AxWrtgzgCd6tdEohnSQNOious5A%3D%3D%0A)
    
      
    ​
    

## Lastly, create a Nightfall AI credential in Tines

1.  Log in to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Nightfall AI credential
    
    1.  **Name:** Nightfall AI
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Nightfall AI** **stories in the [story library](https://www.tines.com/library?s=nightfall%20ai&view=all)**

## "No flows available" Error

##   
**Overview**

The "No flows available" error in Tines occurs when your tenant exceeds its allowable flow limit. A "flow" in Tines refers to a connected set of one or more actions within a Storyboard that can run automatically. Understanding this error will help you manage your workflows more efficiently and continue working without interruption.  
​

## **Cause and solution**

### **Exceeded Flow Limit**

Your organization has reached the maximum number of flows as defined by your subscription. In Tines, an autonomous flow is one that runs on its own without requiring manual intervention. When you've used all available flows, you won't be able to create new ones or restore archived stories with flows.

> 🟢 **Solution**:
>
> -   Archive unused stories to remove their flows from the active count.
>     
> -   Move stories to the "Personal Stories" section, as these don't count against your team's flow limit.
>     
> -   Consider requesting an increase to your flow allocation by upgrading your tenant subscription.

### **Restoring Stories With Connected Actions**

When attempting to restore a story that contains connected actions, you may encounter this error if the total number of flows would exceed your allowed limit upon restoration.

> 🟢 **Solution**:
>
> -   Before restoring, check your current flow usage and ensure you have enough available flows.
>     
> -   Archive other unused stories first to free up flow capacity.
>     
> -   Consider breaking down larger stories into smaller components with fewer flows.

## **Troubleshooting checklist**

☑️ Review your current flow usage in your tenant settings  
☑️ Identify and archive unused or redundant stories  
☑️ Prioritize which stories are essential for production environments  
☑️ Move development or test stories to Personal Stories when possible  
☑️ Contact your Tines administrator about subscription limits  
​

## **Need further assistance?**

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**, when available:

> -   The number of active stories in your tenant
>     
> -   The number of flows you're currently using
>     
> -   Screenshots of the error message
>     
> -   Details about what you were doing when the error occurred (creating a new story, restoring an archived story, etc.)

## Notion Authentication Guide

Notion is a freemium productivity and note-taking web application developed by Notion Labs Inc. It offers organizational tools including task management, project tracking, to-do lists, and bookmarking. Additional offline features are offered by desktop and mobile applications available for Windows, macOS, Android, and iOS. Users can create custom templates, embed videos and web content, and collaborate with others in real-time.

## First, get a Notion secret

1.  Make sure you have admin authorization in your Notion workspace.
    
2.  Login to [My integrations](https://www.notion.so/my-integrations) in the Notion developer portal and click "New Integration"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948720975/2998d2e6d01059a3f3583a4b/image.png?expires=1784851200&signature=8c1243ab47caa382458b1a2e8ab4836daa4be1e261f6361a539bdfdeb695141e&req=fSQvEct%2BlIZaFb4V1XW4gdQwUfe8ms8B9xwEmm9wO%2FM3mirRmwXGJoL9NIi7%0A8yfRu0iJPPiEEUq7zEbZqDBBFw%3D%3D%0A)
    
      
    ​
    
3.  Name the integration, ensure it is connected to the correct workspace and then click "Submit"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948721781/f03ab18751fcc07b7380ae4c/image.png?expires=1784851200&signature=8bb968739425c91e486563817601cb221f7f2af56e19a95f0b32be9f89f24d4a&req=fSQvEct%2FmoleFb4V1XW4gVTySZxs%2FANLQeGY1E7tSRk%2BeS23FG3qugxQ%2BuJ%2B%0AgLcvqFazt8m4djTVt0TCI3H%2Byg%3D%3D%0A)
    
      
    ​
    
4.  Click "Secrets", then click "Show" and copy your Secret to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/948722544/1c27c9f0f837f13b45eed5ef/image.png?expires=1784851200&signature=30777596e3e4f4116be271c830db73c68decad32205ba5862240b0c037e0752b&req=fSQvEct8mIVbFb4V1XW4gdRhhv%2FxmGvghOo5garixV8w9TJBKL%2ByjHDKa8KJ%0Aps17jaf5XGxZpGb3dWVmC0tYjA%3D%3D%0A)
    

## Lastly, create a Notion credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Notion credential
    
    1.  **Name:** Notion
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Secret
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Notion** **stories in the [story library](https://www.tines.com/library?s=notion&view=all)**

## Nutanix Prism Authentication Guide

This authentication guide will cover how to create a credential for the following services:

1.  [Nutanix Prism Central](#h_cd8820ee9b)
    
2.  [Nutanix Prism Element](#h_d3c5cc92e0)
    

## Nutanix Prism Central

Nutanix Prism Central (PC) offers an organizational view into a distributed Nutanix deployment, with the ability to attach all remote and local Nutanix clusters to a single Prism Central deployment. This global management experience offers a single place to monitor performance, health, and inventory for all Nutanix clusters.

To create a Nutanix Prism Central credential, you will need your Nutanix Prism Central:

-   `username`
    
-   `password`
    

### Create a Nutanix Prism Central resource and credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Nutanix Prism Central resource
    
    -   **Name:** "nutanix\_user"
        
    -   **Description:** Optional
        
    -   **Builder:** Your username
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    
7.  Navigate to the team that will be using the API and click "Credentials"
    
8.  Click "+ New Credential" and select "Text"
    
9.  Input the values for the Nutanix Prism Central credential
    
    1.  **Name:** "nutanix\_password"
        
    2.  **Description:** Optional
        
    3.  **Value:** Your password
        
10.  **Optional**
     
     1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
         
     2.  **Access:** What other teams can also use the API
         
11.  Click "Save"
     

## Nutanix Prism Element

Prism Element(PE) is **a service built into the platform for every Nutanix cluster deployed**. Prism Element enables you to fully configure, manage, and monitor Nutanix clusters running any hypervisor.

To create an Nutanix Prism Element credential, you will need your Nutanix Prism Element:

-   `username`
    
-   `password`
    

## Create a Nutanix Prism Element resource and credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Nutanix Prism Element resource
    
    -   **Name:** "nutanix\_pe\_user"
        
    -   **Description:** Optional
        
    -   **Builder:** Your username
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    
7.  Navigate to the team that will be using the API and click "Credentials"
    
8.  Click "+ New Credential" and select "Text"
    
9.  Input the values for the Nutanix Prism Element credential
    
    1.  **Name:** "nutanix\_pe\_password"
        
    2.  **Description:** Optional
        
    3.  **Value:** Your password
        
10.  **Optional**
     
     1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
         
     2.  **Access:** What other teams can also use the API
         
11.  Click "Save"
     

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**

## Okta Authentication Guide

Okta simplifies how users connect to apps and boost your IT team’s efficiency. Modernize your IT with the complete, integrated Okta cloud identity solution.

### First, get an Okta API key:

1.  From the Okta dashboard screen, go to **Security > API > Token** or get there directly by visiting [https://#{{ your\_okta\_domain }}.okta.com/admin/access/api/tokens](https://dev-05937673-admin.okta.com/admin/access/api/tokens)
    
    ![](https://downloads.intercomcdn.com/i/o/753754320/80bd0c8a5c134fe42e4d6c32/Screenshot+2023-04-19+at+5.49.21+PM.png?expires=1784851200&signature=2b598a9574a2b26a1305cc3dd9c44123739261b02fd61f82d664c2abc62f255e&req=cyUkEcx6noNfFb4V1XW4gZYj3wf4wEekM%2BAT1OAAv6HgxSSxl3yFyTwVNWtM%0Aqjvt1Y7pQbu0%2FiTh61tvTp0niw%3D%3D%0A)
    
2.  Give the token a descriptive name, then click “Create token”
    
    ![](https://downloads.intercomcdn.com/i/o/753754618/850d92be6b99f442f78bafe3/Screenshot+2023-04-19+at+5.52.03+PM.png?expires=1784851200&signature=4d6efcb10d517a79aa105afd8cf2ceb95da3eff304a08afe00efe5e5ed2da8b0&req=cyUkEcx6m4BXFb4V1XW4gWAXXJvXEYLjvxMHOhwQIK1Jehm1dlQT8fOjdgRG%0A7qVKO1jj1V4rwZAQq3OCXSt%2Fqw%3D%3D%0A)
    
3.  Copy the “Token Value” and click “OK, got it"
    
    ![](https://downloads.intercomcdn.com/i/o/753754795/84504db2a254bc6852bdcf40/Screenshot+2023-04-19+at+5.54.26+PM.png?expires=1784851200&signature=da7d013241a4a913e9dcababd28d9dbc44780671bb0d0cb76049156b4eed5938&req=cyUkEcx6mohaFb4V1XW4gUmOIbFwG7rjNCsB5FVbV1VAogbML%2BwwVtHth%2BCu%0ANsyOSCCR%2FouWRxNRtgcseA%2BUaw%3D%3D%0A)
    

### Next, create an Okta credential in Tines

**Okta connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Okta" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Okta connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click **Credential**
    
3.  Click **\+ New Credential** and select **Text**
    
4.  Input the values for the credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
    4.  **Metadata:** Select 'Enable metadata' and then paste in the following in Plain code mode. Replace 'yourdomain' with your Okta domain.
        
        ```
        {
          "domain": "yourdomain.okta.com"
        }
        ```
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Read more about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/okta).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Okta stories in the Tines [story library.](https://www.tines.com/story-library?s=okta&page=1)

## OneLogin Authentication Guide

OneLogin, Inc. is a cloud-based identity and access management (IAM) provider that develops a unified access management (UAM) platform for enterprise-level businesses and organizations.

### First, create a OneLogin API credential

1.  Login to your OneLogin account at [https://app.onelogin.com/login](https://app.onelogin.com/login)
    
2.  From the "Developers" menu, select "API Credentials"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/851494282/f4874d3446158c14a5be8f98/image.png?expires=1784851200&signature=5446a12980534fdadca5b974c713bf3ac82da8e91e8f37b7de00b193c1670333&req=fCUmEsB6n4ldFb4V1XW4gR8SxXABqyu4Ll7xS3XTOKsyF0UE6B5hlHDRbBgg%0AHvwvzxVW17A1EGDmdeEkXNmsIw%3D%3D%0A)
    
      
    ​
    
3.  Click "New Credential"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/851512062/cb9d1462c48026c82096ae71/image.png?expires=1784851200&signature=d8a0378643eb7098fa87b1d8fafef2d71f4953020a746c4098e10e32a2789e3e&req=fCUmE8h8nYddFb4V1XW4gVh2KtMP%2FU7i8a%2Fw1hSo3mk7P%2BXIfGXaaTkn5RPI%0AXbrKo3vWserkYJZ4DWRItLKpoQ%3D%3D%0A)
    
      
    ​
    
4.  Name the credential, choose the privilege level, and click "Save"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/851548451/e09f6338967968a21ef0f336/image.png?expires=1784851200&signature=6bef261946d52ca365d985cb912a6f8451125b29ed348f9557928f4b9caba43f&req=fCUmE812mYReFb4V1XW4gRC3%2FYx0ekHp5UUr83K9%2Fy6Bx7tZ%2F6JO57u0WRnQ%0A3qcXnNRjyY3S4SVbqywB5W%2BClA%3D%3D%0A)
    
      
    ​
    
5.  Copy your "Client ID" and "Client Secret" to a text editor and click "Done"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/851549069/7d9b9a7f3a82dcc916913d83/image.png?expires=1784851200&signature=7fec4444c08ad59b0c0c54290eb6d95e28ceb851a94c173d344a569c0f459674&req=fCUmE813nYdWFb4V1XW4gSXUZIp9ECnV4oh4OEAWEvPkRqCaWqSPkxLpBodw%0AoX8Z1y%2B07AZYWsXre38AVVpNAA%3D%3D%0A)
    

### Then, create a OneLogin credential in Tines

**OneLogin connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "OneLogin" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/onelogin).**
>
> **For more on creating credentials in Tines, click** **[here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of OneLogin stories in the [story library](https://www.tines.com/library?view=all&s=onelogin).**

## OpenTelemetry: Designing a Dashboard

## Designing a story troubleshooting dashboard

The goal of the **Tines** **Story Troubleshooting Dashboard** is simple:

> *Make it obvious which parts of a story are slow, erroring, or noisy.*

This dashboard is designed to troubleshoot HTTP-centric Tines stories. It combines span-derived metrics from Prometheus with raw traces from Tempo. You can filter to a single story, drill down into specific destinations or actions, and jump directly from panels into full traces. It focuses on answering: what is slow, what is failing, and whether the issue lives in Tines execution, a particular action, or an external destination.

The dashboard is organized around three ideas:

1.  **Story-level health**
    
    -   high-level RED metrics
        
    -   total requests, errors, duration percentiles
        
    -   quick read on “is this story generally healthy?”
        
2.  **Action-level performance**
    
    -   latency by action
        
    -   error rate by action
        
    -   outlier actions that only occasionally spike
        
3.  **Drill-down paths**
    
    -   click → jump directly into **Tempo traces**
        

### Breaking down the dashboard

#### **1\. Overview**

The Overview section gives a fast, high-level read on the health of a single Tines story. It surfaces core RED metrics such as request rate, error volume, error percentage, average duration, and P99 latency. The intent is simple: tell you, within a few seconds, whether the story is generally healthy or exhibiting failure or latency symptoms.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178477532/6a054ce0d587e27467092fc9198b/image.png?expires=1784851200&signature=7729c22cd62d6f53162cebc503b401384f7d18a03b4ce1e5d600ea3b6131116f&req=diEgHs15moRcW%2FMW3nq%2BgeQMCjydEoDmlbB8BVFyKnnWv8EqmHHDjQ2ya5nM%0AhkPNdu0pyLRcjc2fM8sFAq8RUUw%3D%0A)

#### **2\. Trends over time**

This section focuses on how behavior changes across time, not just point-in-time snapshots. You’ll see throughput, success and error rates, and latency percentiles plotted across the selected time window. This makes it easy to spot patterns such as gradual performance decay, sudden regressions after a deploy, or spikes that correlate with increased traffic. It also helps distinguish sustained latency problems from rare tail events.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178481924/789895e8c74bbeff45a83eee7dfb/image.png?expires=1784851200&signature=fa34b04577477e79b2e247284f11c4bb3ec2ec571a71e880134b05ed96bc91f7&req=diEgHs12nIhdXfMW3nq%2BgQxr5HH%2BhHjCfFo479iyM%2ByZzkW3CDYBd3y0LJGs%0ATEINixvQhEOFWO%2FvfbLIyJum%2BgI%3D%0A)

#### **3\. Error analysis**

The Error Analysis section is designed to answer a single question: what is failing, and where? Errors are broken down by HTTP status class, action, and destination. This helps isolate whether failures are concentrated in one integration, one destination, or one specific action. From here, the dashboard also exposes direct trace pivot links, letting you jump from “error count increased here” into the exact traces responsible for those failures.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178483595/ba6eed9c74de6c7905629d9291e4/image.png?expires=1784851200&signature=3b8704ef820144d1f88722057fac1c0e2cd5c9bdfe3be378fa9f5ee353924a45&req=diEgHs12noRWXPMW3nq%2BgXk2KV87McQ8neMRu%2Fq9A5cW7tC2BMlcmGr7n2fO%0Aoa%2FcAU077LbG%2F2%2BUr6Y4Xl8egkc%3D%0A)

#### **4\. Performance analysis**

This section shifts the focus from failure to slowness. It highlights the actions and destinations contributing the most to overall latency using percentile-based views such as P95 and P99. You can see whether performance degradation is systemic or isolated to a handful of expensive operations. It also surfaces slow-request samples, allowing you to hop straight into slow traces rather than hunting manually in Tempo.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178485285/31b633c69c81c809b9322a6b56c9/image.png?expires=1784851200&signature=949175181db99bc506cdb97901bf653a38047e817fa57ae62da91c4d83b21811&req=diEgHs12mINXXPMW3nq%2BgWigX7ulyPETlilUVOIYy58J3%2BwWFGcmhPEcrXfB%0A4C1%2B5ZGNUXKB%2Bfsq75S5BwDLDtQ%3D%0A)

#### **5\. Volume & activity**

The Volume & Activity panels help explain load characteristics of a story. They show which actions generate the most requests and how frequently specific destinations are called. This is particularly useful for identifying noisy polling actions, unexpected loops, or integrations that are being exercised far more than expected.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178486967/368a92290ee1dee1a0494623f15d/image.png?expires=1784851200&signature=e2c002968ccdbe6c613bc5ad2518cf62a63b3643dbce74b6a3a7dff4eb295559&req=diEgHs12m4hZXvMW3nq%2BgY7yS6mP%2FVgCpRDuRPgQHgsSPwfVW6PxiLNOVYZ5%0AU9P6F7APe4xInbZkC%2FXUvsqXLMc%3D%0A)

#### **6\. Raw events & DB slow query panels**

This section pulls in recent trace samples, not just aggregated metrics. You’ll see tables of the latest erroring requests, slow requests based on your defined threshold, and relevant database-related spans. These provide direct, click-through entry points into Tempo, allowing you to move straight from metric-level symptoms into span-level root cause investigation without guesswork.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178489393/30d8aa6b5739cbf020b665e59cba/image.png?expires=1784851200&signature=c0adc15d1753d56bbeddd30a6c03171af12ca34ee5e67859ac52dca5bf308db0&req=diEgHs12lIJWWvMW3nq%2Bga6m0iSZt6hjp%2BOHYCSVkVW8m3ORXg3%2B8inJuN6g%0AVh80Aa0YhQZvfK1gCWe0IFKizKw%3D%0A)

Continuing on from the example in [this article](https://explained.tines.com/en/articles/14120804-opentelemetry-designing-and-implementing-an-observability-stack), you can find the configurations for the observability stack and Grafana dashboard in the .zip file below:

[tines-grafana-blog-v1-0-0.zip](https://downloads.intercomcdn.com/i/o/am0g137l/2178474801/71a6b9a9c8d6fec60132d4531ae6/tines-grafana-blog-v1-0-0.zip?expires=1784851200&signature=660cf007a70680e29f557d643c36f5b67c60359d8864c243ae77b3b6b7b38955&req=diEgHs15mYlfWPMW3nq%2BgU6Kac8KktPJAfgwfvTEyzwiKYYat7r1hYGgn5hG%0AC%2FGxO6Pvdf8kjINAte8oYbuaXIg%3D%0A)

Read the following articles for help with:

-   [OpenTelemetry: Practical Troubleshooting](https://explained.tines.com/en/articles/14121076-opentelemetry-practical-troubleshooting-walkthrough)

## OpenTelemetry: Designing and Implementing an Observability Stack

## Designing an observability stack

There isn’t a single “right” design. Different teams will make different choices based on scale, security requirements, cost constraints, and what they already run in production.

For this example, the observability design is intentionally simple. The goal is to show how Tines emits traces, how those traces move through the pipeline, and how they turn into something you can actually use in Grafana.

At a high level, the stack is built around five core components:

-   Tines**:** Generates OpenTelemetry traces
    
-   [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): Receives traces from Tines, does light processing and sampling, then forwards them
    
-   [Tempo](https://grafana.com/docs/tempo/latest/): Stores full traces and lets you search and inspect them
    
-   [Prometheus](https://prometheus.io/docs/introduction/overview/): Stores metrics derived from those traces, including RED and service-graph metrics
    
-   [Grafana](https://grafana.com/docs/): The user interface layer that queries Tempo and Prometheus and powers the dashboards
    

Tines sits at the edge, emitting telemetry. Everything else exists to receive it, shape it, store it, and present it in a way that helps you answer questions about performance problems.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178418062/3ae3f3688f1adea0d1eed4469d7f/image.png?expires=1784851200&signature=7b70d9b62f2e57d5b67740db415a2537d12a890a3ac693f8254a45edc0731d71&req=diEgHs1%2FlYFZW%2FMW3nq%2Bga48YAJk3TlxnhI%2FBbWgcvrQmpRkpuAOgDx%2FMqvf%0A7ta3JdYx0JfsaEN%2FK7y4yZCihgM%3D%0A)

This diagram shows the high-level movement of data through our example system:

-   Tines exports OTEL traces
    
-   the OTEL Collector receives and processes them
    
-   Tempo stores and indexes traces for search and TraceQL
    
-   Prometheus stores span-derived metrics
    
-   Grafana queries both and ties traces and metrics together
    

You can find the configurations for the observability stack and Grafana Dashboard in the .zip file below:

[tines-grafana-blog-v1-0-0.zip](https://downloads.intercomcdn.com/i/o/am0g137l/2178421195/26758c35c9dee243d8a769536b53/tines-grafana-blog-v1-0-0.zip?expires=1784851200&signature=d71a09aacc5820c164da4de8f1a5c7e5e9a0a8a31222fe868c1908612558f9d3&req=diEgHs18nIBWXPMW3nq%2BgXPYtwBF8hmwicQpRM8kn5RQmESOCjHrx0MfV9T2%0AdCmVn32vrZVPXjXyCmP%2BHxWIgIA%3D%0A)

> Note on configurations provided in this article.
>
> The configuration examples included in this article are intended to illustrate how an example OpenTelemetry stack can ingest Tines OTEL traces. They are not the only valid deployment pattern and should not be treated as production-ready configurations without thorough self-review. Different environments will use different storage backends, security controls, sampling strategies, and network topologies.

## Implementing an observability stack

Tines needs to be configured to export OTEL Traces to our stack.

At a minimum you need to:

-   enable tracing and auto instrumentation
    
-   choose the OTLP protocol (HTTP or gRPC)
    
-   point Tines at your collector endpoint
    
-   optionally set a service name
    

This means setting container environment variables for tines-app and tines-sidekiq like the below example:

```
environment:
  # Enable OTEL
  - OTEL_ENABLED=true
  # Configure endpoint and protocol
  - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
  - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector-host.example.com:4318
  - OTEL_SERVICE_NAME=tines-app
  # Enable auto-instrumentation for detailed traces
  - OTEL_AUTO_INSTRUMENTATION=true

environment:
  # Enable OTEL
  - OTEL_ENABLED=true
  # Configure endpoint and protocol
  - OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
  - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector-host.example.com:4318
  - OTEL_SERVICE_NAME=tines-sidekiq
  # Enable auto-instrumentation for detailed traces
  - OTEL_AUTO_INSTRUMENTATION=true
```

### Docker Compose: Deploying an observability stack

To keep everything self-contained, we’re running the observability stack with [Docker](https://docs.docker.com/) in a single `docker-compose.yml`. The following diagram shows how all of the components in the stack fit together, including what is exposed, what stays internal, and how data moves through the system.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178428125/81d34bc457d1f2a0169774ebc6d3/image.png?expires=1784851200&signature=9fd23c93d336683e1a0229c0f1cb40c08e59937f5e282e2df3e8ec73b13967aa&req=diEgHs18lYBdXPMW3nq%2BgUiP8BqRaMsYfrEGYgBCQABtu8FrpaLxONbtB3JY%0Aqp0xHAS2wMCXmGk1AD4oshGxKmc%3D%0A)

Below is the directory structure used for this example:

```
tines-observability/
├─ docker-compose.yml
├─ .env                                  # .env file
├─ grafana/
│  └─ provisioning/
│     ├─ datasources/
│     │  ├─ prometheus.yml               # Grafana Prometheus Datasource
│     │  └─ tempo.yml                    # Grafana Tempo Datasource
│     └─ dashboards/
│        └─ story-troubleshooting.json   # Grafana dashboard json(s)
├─ tempo/
│  └─ tempo.yaml                         # Tempo config
├─ prometheus/
│  └─ prometheus.yml                     # Prometheus scrape config
├─ otel-collector/
│  └─ config.yaml                        # OTEL Collector config
└─ data/                                 # Bound as volumes
   ├─ grafana/                           # /var/lib/grafana
   ├─ tempo/                             # /var/tempo (trace blocks)
   └─ prometheus/                        # /prometheus (TSDB)
```

### OTEL collector: Configuration, filtering, & exporting traces

The OpenTelemetry collector is the center of the pipeline. Tines sends traces here, the collector decides what to keep, generates metrics from those traces, and then forwards everything to the right backend.

### Receivers: Where Tines sends the data

The collector exposes both OTLP endpoints:

-   **gRPC on 4317**
    
-   **HTTP on 4318**
    

You can choose either protocol in Tines, so both are enabled:

```
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317 # GRPC Receiver
      http:
        endpoint: 0.0.0.0:4318  # HTTP Receiver
```

### Filtering: Remove noise before sampling

Before sampling or exporting, the config drops spans you will almost never care about. In our example we’re dropping the /health endpoint, but you can filter more aggressively as needed:

```
  filter:
    traces:
      span:
        - 'attributes["http.target"] == "/health"'
```

> Note: Tines has [additional recommendations for head based sampling](https://www.tines.com/docs/self-hosting/additional-configuration/exporting-opentelemetry-traces/#optional-sampling-auto-instrumentation) for auto instrumentation, as it can be extremely noisy. An efficient and useful production sampling configuration likely employs a combination of both head and tail based sampling.

```
  # Tail-based sampling - decides AFTER seeing complete traces
  tail_sampling:
    decision_wait: 20s  # Wait time for spans before sampling decision
    num_traces: 20000         # Max in-flight traces considered for sampling
    expected_new_traces_per_sec: 75  # Used to size internal reservoir/buffers

    policies:  # Evaluated in order, first match wins
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]  # Keep 100% of errors


      - name: slow-traces
        type: latency
        latency:
          threshold_ms: 5000  # Keep 100% of traces >5s

      - name: fast-traces
        type: probabilistic
        probabilistic:
          sampling_percentage: 10  # Sample 10% of remaining "normal" traces
```

### Batch & memory protections

Two processors are here largely for operational safety:

-   `batch`: send spans in groups instead of individually
    
-   `memory_limiter`: keep the collector from running out of memory
    

```
  # Batch - groups data before export to reduce network overhead
  batch:
    timeout: 10s             # Max time to wait before flushing a batch
    send_batch_size: 1024    # Preferred batch size (items)
    send_batch_max_size: 2048  # Hard cap on batch size

  # Memory limiter - prevents OOM by applying backpressure
  memory_limiter:
    check_interval: 1s
    limit_mib: 1536  # Soft cap for collector memory inside 2GiB container
```

### Spanmetrics connector: Turning Traces into Metrics

The spanmetrics connector converts traces into Prometheus-queryable metrics. It does three main things:

1.  It builds latency histograms using explicit **buckets**, which lets us calculate percentile latency (like P95/P99) and quickly spot slow or timing-out requests in the dashboard.
    
2.  It enables **exemplars**, so individual metric points can link directly back to the exact trace in Tempo that caused them, making “jump to trace from spike” possible.
    
3.  It adds selected **dimensions** (HTTP method, status code, destination host, exception type, story and action identifiers) so we can break metrics down by specific actions, destinations, or failure types without exploding cardinality.
    

```
connectors:
  spanmetrics:
    histogram:
      explicit:
        # Extended buckets for better P95/P99 accuracy and timeout detection
        buckets: [100ms, 250ms, 500ms, 1s, 2s, 5s, 7s, 10s, 12s, 15s, 20s, 30s, 45s, 60s]

    exemplars:
      enabled: true # Link metric samples back to example traces in Tempo

    metrics_flush_interval: 15s

    dimensions:
      # HTTP attributes
      - name: http.method
      - name: http.status_code

      # Network attributes
      - name: net.peer.name         # Destination hostname

      # Error attributes
      - name: exception.type        # SSL / timeout / connection error groupings

      # Tines-specific attributes
      - name: action.type           # Agent type (HTTPRequestAgent, etc.)
      - name: action.id             # Specific action within story
      - name: story_container.id    # Container/folder ID

      # Database attributes
      - name: db.system
```

### Exporters: Where data actually goes

Traces → Tempo

```
  # Send traces to Tempo for storage and querying
  otlp/tempo:
    endpoint: tempo:4317
    compression: gzip  # Compress traces before sending
    sending_queue:
      enabled: true
      num_consumers: 4 # Parallel senders
      queue_size: 10000 # Buffer during Tempo hiccups
    tls:
      insecure: true  # No TLS for internal Docker network
    timeout: 30s
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s  # Give up after 5 minutes
```

Metrics → Prometheus

```
  # Expose metrics for Prometheus to scrape at :8889/metrics
  prometheus:
    endpoint: "0.0.0.0:8889"
    namespace: tines  # Prefix: tines_traces_span_metrics_*, tines_otelcol_*
    const_labels:
      environment: production
    resource_to_telemetry_conversion:
      enabled: true  # Promote resource attrs (service.name, etc.) to metric labels
```

### Service pipelines: wiring it all together

At the bottom of the config, everything is connected:

```
service:
  telemetry:
    logs:
      level: info
    metrics:
      level: basic  # Enable otelcol_* self-metrics

  pipelines:
    # Traces: OTLP → mem limit → filter → tail sampling → batch → Tempo + spanmetrics
    traces:
      receivers: [otlp]
      processors: [memory_limiter, filter, tail_sampling, batch]
      exporters: [otlp/tempo, spanmetrics]

    # Metrics: spanmetrics + prometheus receiver → batch → Prometheus exporter
    metrics:
      receivers: [spanmetrics]
      processors: [batch]
      exporters: [prometheus]
```

### Tempo: Trace storage

Tempo is the trace backend in this stack. The OpenTelemetry Collector sends sampled traces to Tempo over OTLP, and Tempo is responsible for storing them and making them queryable from Grafana.

This configuration does a few important things:

-   Tempo is configured to listen on:
    
    -   gRPC on `4317`
        
    -   HTTP on `4318`
        
        -   These ports are internal to Docker. Tines never talks to Tempo directly — traces always go Tines → OTEL Collector → Tempo.
            
-   It uses local storage with WAL + block compaction
    
    -   a write-ahead log (WAL) at `/var/tempo/wal`
        
        -   WAL is the short-term buffer while traces are being ingested
            
    -   immutable **compressed blocks** at `/var/tempo/blocks`
        
        -   blocks are the long-term persisted storage
            
        -   the compactor periodically merges and deletes old blocks
            
        -   Retention is explicitly configured: `block_retention: 168h` → 7 days of traces
            
-   **It runs the metrics generator**
    
    -   This config enables Tempo’s built-in metrics generator. The generator consumes traces and produces:
        
        -   service graph metrics
            
        -   exemplars tied back to Tempo traces
            
    -   Those metrics are sent to Prometheus via `remote_write`: [`http://prometheus:9090/api/v1/write`](http://prometheus:9090/api/v1/writew) with `send_exemplars: true`
        

### Prometheus: metrics from traces

In this stack, Prometheus stores metrics that are generated from traces\*\*:\*\*

-   Tines sends traces to the OTEL Collector
    
-   the Collector turns those traces into metrics via spanmetrics
    
-   Prometheus scrapes those metrics and stores them
    

### Grafana: datasources and dashboards

Grafana is the UI layer in this stack. It does two things for us:

-   connects to **Tempo** for traces
    
-   connects to **Prometheus** for metrics
    
-   optionally loads **prebuilt dashboards automatically**
    

Instead of clicking everything together in the UI, we deploy Grafana declaratively so the environment is repeatable.

### Datasource provisioning

**Tempo datasource**

-   type: `tempo`
    
-   URL: [`http://tempo:3200`](http://tempo:3200)
    
-   used for:
    
    -   TraceQL search
        
    -   viewing full traces
        
    -   jumping from metrics → trace
        

It’s also wired so Tempo can pull metrics context from Prometheus for service maps and node graphs.

**Prometheus datasource**

-   type: `prometheus`
    
-   URL: [`http://prometheus:9090`](http://prometheus:9090)
    
-   marked as **default**
    
-   sample interval set to `15s` to match scrape interval
    
-   exemplarTraceIdDestinations tells Grafana:
    
    -   exemplars in Prometheus metrics contain a **trace ID**
        
    -   clicking them should open **Tempo**
        

### Dashboard provisioning

The dashboard provider configuration tells Grafana to:

-   look for JSON dashboards in
    
    `/etc/grafana/provisioning/dashboards/json`
    
-   put them in a folder called **Tines**
    
-   automatically reload if files change
    

Read the following articles for help with:

-   [OpenTelemetry: Designing a Dashboard](https://explained.tines.com/en/articles/14120923-opentelemetry-designing-a-dashboard)
    
-   [OpenTelemetry: Practical Troubleshooting](https://explained.tines.com/en/articles/14121076-opentelemetry-practical-troubleshooting-walkthrough)

## OpenTelemetry: Practical Troubleshooting Walkthrough

> *I have a story in my Tines instance that i’ve noticed has been running erratically, erroring, and overall slow.*

Based on the above problem, we should be able to answer the following:

1.  What are the overall RED metrics for my story?
    
2.  What Action(s) and Destination(s) are encountering errors or exceptions in my story?
    
3.  What Action(s) are consistently slow in my story?
    
4.  How can I easily see what is consuming HTTP request run times?
    

## Overall: RED metrics

This is pretty easy to see from the overview section. We can clearly see that my story, over the last hour, has had an extremely high error rate and high average request duration.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178514446/1984a12a550aecd22f9b30ee7faf/image.png?expires=1784851200&signature=7e45af81e61a974467b98d09b878df6cc69955c38f8e9a11458b0380a06e77d4&req=diEgHsx%2FmYVbX%2FMW3nq%2BgXLpywqHdI8LJ6hL2bu7UsMg1O%2BxZiOAlqeaGFd6%0AK2RQDEifRZTWG0kILz8db0VHohQ%3D%0A)

We can see that my request volume over time has been consistent, with spikes correlating to when the story is scheduled to run.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178515853/dfdea5096c4b867236fc1b5843a4/image.png?expires=1784851200&signature=19c9e4a3d7a9bc619c46358519daa30950057b06d346e38b327104676758ad0b&req=diEgHsx%2FmIlaWvMW3nq%2BgT9Su3IlDhAFgGBfrAeG4lhOM7H9V4TCufGr%2Flad%0APaOX%2FARyDXPX2X%2FVF%2BaWGjulkO4%3D%0A)

We can also see that the story has had a consistent error rate each run, meaning there is likely a set of or singular action(s) that are having issues. The high request duration percentiles also indicates that one or more actions are running slowly.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178520650/22ac316760ba8428b26e551e0a83/image.png?expires=1784851200&signature=207c2b4e675393d51e334f7d5fed82d5133eaaa11ce86c365cbd0f78bd289dad&req=diEgHsx8nYdaWfMW3nq%2BgVGOgC6sZ2yso3chO%2B5dXpPag8I9JkREYDsBNicN%0Avu66K9I%2F320TYqvmYVk2toMP0sU%3D%0A)

### Actions & destinations: Errors & exceptions

Now I need to see which actions and destinations are the most problematic in my story. This Trend graph over time shows that I am consistently receiving 4 main errors:

-   4XX errors
    
-   5XX errors
    
-   SSL Errors
    
-   Timeout Errors
    

So which actions are the problem?

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178526802/bdf0fd42db90cd0b2adf30e00f72/image.png?expires=1784851200&signature=e143afe4c7b1255b7bb0c65f6b54b4a8728147c5a5d66d3f9d7bea4b8d0f1f0b&req=diEgHsx8m4lfW%2FMW3nq%2BgZFb02vtkBGKqXCiLcMBQIU%2BUynXXRQr0qoXOVNx%0A4jUhAUYhbffHCpBcNhJAwzrAbw8%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178527712/f0e6f6b85fae03db2b3e282f1ab2/image.png?expires=1784851200&signature=11990251f1baf66eb1e77e6017c4f664ffdca5d00fb1d5f3e1296fdb10ac5836&req=diEgHsx8moZeW%2FMW3nq%2BgQSd%2BGqMixO3uZl%2BJ2S%2B7jqR%2BkgiTGbyHGwTuylP%0A8LyKoyzXBJKqqq1Li2JCEvx8zhk%3D%0A)

I can see that action ID 2057 is my highest error count, clicking into the Trace link for that action, I can easily find out that the action is generating a lot of 504 errors.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178528849/d02bad680fc49932435479cda5e3/image.png?expires=1784851200&signature=f135bb682f15edfbc2572bf63075a77c918f6a146c18c65026694f60d1d07a42&req=diEgHsx8lYlbUPMW3nq%2BgexAsyB1Cpz3GOSqg8iCyXl8wsepwgOBRCOFN2Dg%0AYP%2FRcoM%2BKnCC4lBiGII%2BNXSZUS4%3D%0A)

Checking the action logs in my story confirms that this action is receiving 504 errors, so I can now investigate deeper as to why!

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178529618/f3b8c8eea28318dd1a5bc0f85de4/image.png?expires=1784851200&signature=2daa599afa2e5722ee767a29a2a197f3b1efc7219a2dc3cc45f27ea8f80e7c52&req=diEgHsx8lIdeUfMW3nq%2BgTgd67KOBXwJhIy7rsl2fGYUgRn4HColTzBmxdJe%0AVHrDlyAFRbh8f%2BoCYSZod6Kt2G0%3D%0A)

Similarly with destinations, I can see that self-signed.badssl.com & [expired.badssl.com](http://expired.badssl.com/) are receiving exceptions. Clicking into expired.badssl.com and drilling into a span shows me that the service for this destination is returning an expired certificate. Within the span attributes we can then see the action id for this problematic action is 2033.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178531084/acf1a51f41188e53ef0649b4620c/image.png?expires=1784851200&signature=bc026022b34629f0332bdc4dc8850a86260ea429122a6f102ed7a3c8449ae4a8&req=diEgHsx9nIFXXfMW3nq%2BgRaH2sEnX3eDKBwF1g%2FfG8d%2Bjl9m5NDM9uebQmsz%0AX5QmMbCxLqCoq44BqgEqEz97kVc%3D%0A)

### Actions: High latency

Now that i’ve identified all the errors in my story, I want to find which actions are slowing down my story execution. Looking at the performance analysis section shows me my average latency is high, but particularly so for tines-mbp.local.

Drilling into the span for the slowest action, I can see that the majority of the time spent for action 2035’s execution was actually just the http request to the service and timed out. We can see prior and subsequent operations (like database calls) were very quick!

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178797683/5a5f04cf8df0ba8bcefaedefc06b/image.png?expires=1784851200&signature=41e56b2a041fd4d921289c877a6ba195781682ab1d7a0aa7f6d340741c313f26&req=diEgHs53modXWvMW3nq%2BgT3J7cQmf02dwEjKrCTkOKvl%2F0VT4MxYoV0qk4a%2B%0AxyUphUfE%2FA7bw6W9WjtxgJC115Q%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178797937/0f3fa897c21f3525f266db28f5e9/image.png?expires=1784851200&signature=1e0ff03a09d2c876095e19eb730610a79ea4cb7d85c8def1ed19792d17e20d8d&req=diEgHs53mohcXvMW3nq%2Bgb08HFHYLx3mYZRsSKiaDx6i7GMXjyTC6QUF7dSl%0A6NSOYphtMDvO5CiX1cjtx4Iyu9c%3D%0A)

### HTTP requests: Where is the time spent

I’m also suspicious that a normally fast set of actions has been particularly slow. I’ve confirmed that the route and endpoint work very quickly normally. In this case, I can check the slow tines-app DB queries in sidekiq jobs panel to check if there any requests are slow because of the database, and not the http request. We can see I have a few spans that fit the criteria!

Drilling into the span, we can see the majority of this HTTP POST request was actually spent inside of the database, and not the POST itself!

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178799359/c9118a0e81aa9306749a19086e48/image.png?expires=1784851200&signature=2dceca6aa61af377b4aff00e3537e5c7761c2ebf9d2db78195103491f381a88d&req=diEgHs53lIJaUPMW3nq%2BgRotr37Dcq2iY%2Fdd3wCHQ%2FVPkOo2nfJAtGw7xkO9%0AEtMIZgbzLBfhAzNoqYUW3VkGu3Y%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2178799487/428fcf83ed596aa328e564cadb51/image.png?expires=1784851200&signature=b79fb3781c9799d97b890c03ba8a05d982113e38f7d8253070cee0d2908d1a20&req=diEgHs53lIVXXvMW3nq%2BgYArB9oejyTTyxU%2FV5VtQuvNvVdHzJvy9xZIUfpD%0A6dZFM7FZXNUvjI8djNfb9IRnGeg%3D%0A)

Continuing on from the example in [this article](https://explained.tines.com/en/articles/14120804-opentelemetry-designing-and-implementing-an-observability-stack), you can find the configurations for the observability stack and Grafana dashboard in the .zip file below:

[tines-grafana-blog-v1-0-0.zip](https://downloads.intercomcdn.com/i/o/am0g137l/2178903633/6cf0f830ed2b31f0cd62329c42d7/tines-grafana-blog-v1-0-0.zip?expires=1784851200&signature=5465d9b144a848caca867f4ef213ef11e910efd040b9bf719e969b857cd44232&req=diEgHsB%2BnodcWvMW3nq%2BgZT9tnYq4E0Ygj7mb%2BXgDpT%2B2FLVM3DPqky1hjr4%0A5mnanhPP7%2BTKvN7JRypx4cYlwsU%3D%0A)

## Orca Security Authentication Guide

### First, create a Orca Security API token

1.  Log into your Orca Security dashboard [app.orcasecurity.io](https://app.orcasecurity.io)
    
2.  Log in to your Orca Security tenant
    
3.  Select the **Settings** icon on the left side
    
4.  Under **Connections**, select **Integrations**
    
5.  Select the **API Tokens** tile by clicking **See API Tokens**
    
6.  Determine the expiration date, role, and scope you wish to apply
    
7.  Click **Add**
    

### Lastly, create a Orca Security credential in Tines

**Orca Security connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Orca Security" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/orca-security).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Orca Security stories in the [story library](https://www.tines.com/story-library?s=orca+security&page=1)**

## Pages FAQs

## How do I configure a Tines page with SSO (Okta)?

There are three steps to share a page with your organization in Tines via Okta.

1.  Ensure that Tines has been assigned to the colleagues who need to access the page
    
2.  Ensure that SSO is configured for your tenant. The option to select SSO will not appear unless SSO is configured for your tenant.
    
3.  Set the page as "Public" and tick the "Require SSO" option
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218691957/d5545f8924410a9d604e56b7ffe8/209e9ee8-1e54-4245-8dff-830110976d30?expires=1784786400&signature=aad35585a69b205bf252e609ca189abf82ba336801748742659670de674bdc48&req=diImHs93nIhaXvMW3nq%2BgWn069ODoof0JmBSFynXQWaMa9Ms9CtOwDZBX%2FB2%0AzsPdP0iHSjtQSya8Oo5m2djYD%2Fw%3D%0A)

## Does Tines Pages support vanity URLs?

Tines currently does not support vanity URLs.

> Read more about pages in our docs [here](https://www.tines.com/docs/pages/).

## Palo Alto Networks Firewall Authentication Guide

To generate an API key you will need the following:

-   The firewall's hostname or IP address
    
-   The firewall's admin username
    
-   The firewall's admin password
    

## First, create Palo Alto Networks Firewall credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Palo Alto Networks Firewall credential
    
    1.  **Name:** PAN Firewall Admin Password
        
    2.  **Description:** Optional
        
    3.  **Value:** Your admin password
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

## Next, generate a Palo Alto Networks Firewall API Key in Tines

1.  Create a new story
    
2.  Paste the following action onto the storyboard, replace `firewall_hostname` and `<user>` with the firewall's hostname and admin username, then run the action  
    ​
    
    ```
    {"standardLibVersion":"42","actionRuntimeVersion":"6","agents":[{"disabled":false,"name":"Generate PAN Firewall API Key","description":"Created from cURL command","options":"{\"url\":\"https://<<firewall_hostname>>/api/?type=keygen\",\"method\":\"post\",\"content_type\":\"application/x-www-form-urlencoded\",\"payload\":{\"password\":\"<<CREDENTIAL.pan_firewall_admin_password>>\",\"user\":\"<user>\"}}","position":{"x":1995,"y":375},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:cf6e110a4849cd0f6b69a3d39da8f81a"}],"links":[],"diagramNotes":[]}
    ```
    
3.  Copy the `key` value from the response:  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/943248188/ca8ea5d69450d6ebc8b60f1f/image.png?expires=1784851200&signature=a0f7096eab2a877c45f493fa12a1fa48813782e7b750403446a612c593672400&req=fSQkFM12nIlXFb4V1XW4gUS3FTGNDENQ3fzRndP4G%2FrR5JLAGORyvIdDaH2f%0AoVXM8pkFK0YEecv0DI3gvZSOGw%3D%3D%0A)
    
      
    ​
    

## Lastly, create Palo Alto Networks Firewall credential in Tines

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the Palo Alto Networks Firewall credential
    
    1.  **Name:** PAN Firewall API Key
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API Key
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Palo Alto Networks Firewall** **stories in the [story library](https://www.tines.com/library?s=palo%20alto%20networks%20firewall&view=all)**

## Payloads in Tines explained

### **What are payloads?**

Payloads are a way to send structured data as an event in Tines. Think of payloads as the messages sent between your web browser and a website or application. Where the HTTP request is the transfer of the information, the payload is the actual information being transferred. You can send data using payloads to:

-   another system via an API
    
-   another story via a send-to-story action
    
-   or to another action within a story via our event transform action
    

### **How can I use payloads in Tines?**

You can use payloads with 3 of the 8 action types in Tines: HTTP requests, event transform message-only mode, and send-to-stories. In the case of HTTP requests, a payload can be the data sent with a HTTP request or the data returned from a HTTP request. So if we are making a request to a weather API to fetch the weather, the payload can be the request for specific weather information, or it can be an actual forecast that is returned.

You can configure the request as a key/value option in the action builder like this:

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/977863909/b08b7750e8ecb16458b38be8/SGrX98hWIpAf8sIpbhxJ52b0vB-lCut5-EEuocQILmhqgjrcC01Cjq5D3BVXs4pCmUPqG1jRe_cN8O8pe0s4BHlFyRy3wRWADzqeOpfzD48HjRGlCRfk8r1R5N5zc7-0hii4f1SSCv6a94YWtMLgvsQ?expires=1784851200&signature=70bfc0ac7699883a5185f631f809d55753ac08e87740748953968bb3921632c1&req=fScgHs99lIFWFb4V1XW4gZxALiN3vtyHNSb2gdj3HcgGCMrty6O36bCGESZt%0A3S1cDXgBnffmgEwoO0%2B38q8ZWA%3D%3D%0A)

In plain code, that would look like this:

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/977863925/08cc72a4fc988003954941ff/OtQLRy1Q54rXnvXgk8VCu0jdUqEqbrnm9KtXzadpCzwzT7Zl0wD37wPsalxudZNxjEm521eLPIJgLfaC2IyD14zWvBphnFCWzsdRQLGkrlh4ijg35nRMhA8pU1W1NvTcT0TpJn7x6Ki48_VorxW6p_k?expires=1784851200&signature=cf4b1fc8bd456c6da0bc555be187d20065c91351687a7f7c42d7cf1037abce7f&req=fScgHs99lINaFb4V1XW4gXYjcLhVtewYl7Z5n%2FjmOZ%2B8BT%2BNv3fIRjsz3v8V%0Awp%2F59IeTqIRNXkldYtrfpCcnFw%3D%3D%0A)

In the case of the information returned, a payload might look something like:

`{`

`"city": "dublin",`

`"forecast": "broken clouds"`

`}`

In Tines, when configuring a payload request, you have a number of different data configuration options to choose from. The value option determines the structure of the data in the payload. The different options are; text, boolean, null, numbers, arrays, and objects.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/977863946/18a45db9576ff9b369cdfed7/CWujOVj6YdM4Z_2TBsJVhoWj43oG-HGJytugpfnLwh3Qw9Hd_hwX1SnGUHURWmJVViscdrxTZ_Pnd51T7wvOJK9npFZ2TMr0u8WZAm5hEsTg64T1SfrX6t45KCOe1NxHYYvzKqwScrX8QPBrAglr7QY?expires=1784851200&signature=f082dba5489e9a64bdc304380be6ea6082c063a2ef8cc24dbf17907f39199efb&req=fScgHs99lIVZFb4V1XW4gfxzl2xmdqvhlYLE8do15iaYC3x7LLey0tn2FhSW%0Aqh0u6VvH2Gue8n3ioMboCB1Rrw%3D%3D%0A)

Here is a guide for when to use each payload type.

| Value options | **Description** | **Supports** | **When to use this mode** |
| --- | --- | --- | --- |
| Text | Values will be automatically converted into text regardless of their initial data type. | 
-   Hard-coded text
-   Functions
-   Tags

 | Use this mode when the output of any functions or tags applied should be text or if you are simply typing in text. |
| Formula | Allow values to retain their initial data type. | 

-   Functions

 | Use this option when the output of any functions applied should retain their initial data type (e.g. array, object, integer, boolean). |
| Number | Only accepts numbers and retains their integer data type. | 

-   Hard-coded text

 | Use this option when you want to hard-code a number into a payload. If you’d like to use a formula that should have a number output, use the formula option. |
| True / False | Only accepts boolean values true or false. | 

-   Hard-coded text

 | Use this option when you want to hard-code a boolean (true or false) into a payload. If you’d like to use a formula that should have a boolean output, use the formula option. |
| Null | Only accepts the value null. | 

-   Hard-coded text

 | Use this option when you want to hard-code the value NULL into a payload. If you’d like to use a formula that should have a null output, use the formula option. |
| Add an object | Allows you to create a JSON object or series of nested JSON objects with key / value pairs in Tines. | 

-   Hard-coded text

-   Functions

-   Tags

-   Any of the other value options in this table.

 | Use this option to create a JSON object in Tines. This is often helpful for building API calls and structuring your JSON data. Please note, you can also create an object within a formula when using the formula value option in payloads. |
| Add an array | Allows you to create an array or series of nested arrays in Tines. | 

-   Hard-coded text

-   Functions

-   Tags

-   Any of the other value options in this table.

 | Use this option to create an array in Tines. This is often helpful for building API calls and structuring your JSON data. Please note, you can also create an object within a formula when using the formula value option in payloads. |

> **👉 Check out our product docs on HTTP requests [here](https://www.tines.com/docs/actions/types/http-request).**

## PDF Generator Authentication Guide

PDF Generator API allows you easily generate transactional PDF documents and reduce the development and support costs by enabling your users to create and manage their document templates using a browser-based drag-and-drop document editor.

## First, get PDF Generator credentials

1.  Log in to your PDF Generator account
    
2.  Click "Settings" and copy your "API key" and "Secret" to a text editor
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910258233/2a9a07d09a7ee9cc28a84c4a/image.png?expires=1784851200&signature=26eb293f184f0937c3a0088af29222a84b608c17ab48f86b736b35f90e15922d&req=fSEnFMx2n4JcFb4V1XW4gT%2FTVoBw%2Fp6Nlz0GRa7brh3XhNT0WdFreshhcBG%2B%0AgS4l2kj8knvsHDeecw2T8Yxv6Q%3D%3D%0A)
    

## Lastly, create a PDF Generator credential in Tines

You will need to create:

-   One Resource: "PDF Generator username"
    
-   Two Credentials:
    
    -   Text type: "PDF Generator API key"
        
    -   JWT type: "PDF Generator"
        

##### Resource

1.  ##### Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the PDF Generator resource
    
    1.  **Name:** PDF Generator username
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your PDF Generator username
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

##### Text type

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the PDF Generator credential
    
    1.  **Name:** PDF Generator API key
        
    2.  **Description:** Optional
        
    3.  **Value:** Your PDF Generator API key
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

##### JWT type

1.  Log in to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "JWT"
    
4.  Input the values for the PDF Generator credential
    
    1.  **Name:** PDF Generator
        
    2.  **Description:** Optional
        
    3.  **Algorithm:** HS256
        
    4.  **Payload:** Copy the below object and paste into the "Plain code" section:
        
        1.  Replace `<unix_epoch_time>` with the unix epoch time timestamp of how long the the token is valid for.
            
            -   It is highly recommended to set the `exp` timestamp for a short period, i.e. a matter of seconds, that way if a token is intercepted or shared, the token will only be valid for a short period of time.
                
        
        ```
        {
          "iss": "<<CREDENTIAL.pdf_generator_api_key>>",
          "sub": "<<RESOURCE.pdf_generator_username>>",
          "exp": "<unix_epoch_time>"
        }
        ```
        
    5.  Tick "Auto generate 'iat' (Issued At) & 'exp' (Expiration Time) claims"
        
    6.  **HMAC Secret:** Your Secret
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/910262576/815fe6999802604f9413d8bb/image.png?expires=1784851200&signature=e5cb4162d1689075f83d417be40ab30a594593ff42b5affd8a507fdad5f47174&req=fSEnFM98mIZZFb4V1XW4gVwOkelU9YUOGnVEN7Yd09G%2BhZjt2IMXLsKm4YFA%0AJjAiBTYVTbVCFkvrV%2FdIbSwFHg%3D%3D%0A)

​

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of PDF Generator stories in the [story library](https://www.tines.com/story-library?s=pdf%20generator)**

## Power BI Authentication Guide

Power BI is a tool that you can connect to and generate reports using your own data sources. This article will walk you through setting up a Power BI credential in Tines.

## Create a Power BI credential in Tines

In your Tines tenant, navigate to the credentials section for the Team you want to create the credential in.

Select “+ New” > “Manual Creation” > “OAuth 2.0” > “Create Credential”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195179682/61ce205e4497505d985ff31b5690/Manual+credential.gif?expires=1784786400&signature=ef2c60d01b5ee3e9d7f4e1d51f5bea865cc3efc00c5783be40e74be1ae898a3c&req=diEuE8h5lIdXW%2FMW3nq%2BgbIuYNuHmhFyKnCIUagr%2F%2FIZdbtwx6X8p99FHP2p%0AMsRxIMnSOo7QN83BElbZItv0KlE%3D%0A)

Set name as “PowerBI” and product as “PowerBI”.

Copy the callback URL for later (i.e [https://tenant-name.tines.com/oauth2/callback](https://tenant-name.tines.com/oauth2/callback)).

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195181198/0f479abbc5091a2b1cfe296d672e/eea30e3e-aa7f-4048-84af-d3266db92a98?expires=1784786400&signature=55058ab747165f9d8f333d80175ee75b033fa62c8839758ebabf029f323b6d7d&req=diEuE8h2nIBWUfMW3nq%2BgaqqzHUoaanshJ4DXu89IjJJrBGfEdOwOMNndtoM%0AAoaStjGsrTp8v1IV039xKZCJneA%3D%0A)

> Note: There are other credentials we need to create first before we can save. Keep this tab open while you continue through this guide.

## Create Azure application

> Note: If you have already created an Azure application for your Microsoft Graph credential, you could skip the application setup portion and just edit the permissions to include the PowerBI scopes/permissions.

Otherwise, or if you would like to create a separate app for PowerBI, follow the next steps.

Navigate to [app registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) in Azure. Give the app a name, select the your desired supported account types (“Single tenant only” was used in this example).

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195185862/d890fea1dee95e0a890cbbf7b11a/3b6be887-c679-40ee-9dc7-36005109faae?expires=1784786400&signature=787f7a6795aa4c0a09d58f3c1805ac17d8b63464dd8e04b9cf0a8849f496cb34&req=diEuE8h2mIlZW%2FMW3nq%2BgbMqPYUtlV08tKRiVgwMLTda6YpKuNUZV0a1W76F%0ACpXhzoZXh%2BT45r8uArRn5%2FCi9PQ%3D%0A)

> Note: The "Help me choose" option gives descriptions for the account types.

Under Redirect URI, select “Web” for platform type and paste the callback URL from Tines into the. Click “Register”.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195186751/38712703da5daf9044d20a84b500/676fc300-fcab-430a-a91c-9e7b49c8c4cf?expires=1784786400&signature=ecff6e5abcefe6438bfdac724b5450fefed51ead8e49c5969c24cfd3c2af9501&req=diEuE8h2m4ZaWPMW3nq%2BgRlVg%2FbTcdNjZ7aZvB9speNnVh%2BzCZ73thXe6bRg%0A5itxk7VcGKtu%2FOVs2e5dGS0G6e0%3D%0A)

Within your app, navigate to Manage > Certificates & secrets and click on the Client secrets tab.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195186752/e0f4d1b620c06e055da62b929728/436c72a0-d657-48c0-8c7d-d601857203dd?expires=1784786400&signature=d0984d25d66f87831cb2828e870dc857964c6a24cdb49171c9298787d09fdafb&req=diEuE8h2m4ZaW%2FMW3nq%2BgSIs3fws52%2BczIQKnrN3R1ZjB3JrobtVPu4oOrNF%0A6I%2F81pUwfHHThwDduWcb%2FTfPiG4%3D%0A)

Select “+ New client secret” to name and set expiration date. Note the expiration date for later. Click “Add” to generate a new secret.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195203774/29f5f6c7555dd8b147f6551c1863/d1b8f5df-decd-40c5-9299-c92a817e722e?expires=1784786400&signature=64769e04246af350084f2d1fc7c238addfc9b99a8a3bbf87f20409e05b55896d&req=diEuE8t%2BnoZYXfMW3nq%2BgbJMY69MyBVSY%2FicLBND7zOdYvS3urqzCxIpax5S%0AwC5RKz91EllEDKo7E%2FzCE5KqVx8%3D%0A)

Copy the secret value to use later in our Credential in Tines.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195205672/80dca211c29b0b0b3a8d951afec4/d8755e14-d107-4c8c-bc70-7974290f650b?expires=1784786400&signature=d5c4f49ded8bbff4351c3bbeafa72c68fc48ba428a9e9e75d0f41f74a052d05a&req=diEuE8t%2BmIdYW%2FMW3nq%2BgXynnJKalARccGLi548o4t1LE%2FV%2B%2BwPzX7W6I2Y4%0A1%2Fx1FOflRVbv4Amre4G9o1HFKig%3D%0A)

Navigate to API permissions and click on “+ Add a permission” then scroll down to select “Power BI Service” to add the relevant Power BI permissions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195205675/fa9f8b8a6297ee38653ef19ec339/f02c912d-3667-49ca-bd8e-2f35706a9aae?expires=1784786400&signature=2d3055adcf49d7ef6d6660c33caf03b55cba04492932825d041d8149a8a8540e&req=diEuE8t%2BmIdYXPMW3nq%2BgYLWA6v%2BYrQR09wP16%2BO%2FhcJEjrIy4VwhReMtc3t%0AbM8j2yMJi4XxFpkzssGSczUZU9k%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2195205673/e23143f01cc1a7a020b0cf24be2c/27946cfd-d4a3-4cf8-bbfb-819711067109?expires=1784786400&signature=6d0aaef605264a9fe4883e6073ecb88a3af7f14df3ee7af913d53e4c60f24d78&req=diEuE8t%2BmIdYWvMW3nq%2BgW75IYG1QRyA72P9lKuzWdtPdukrsW5D3a7NrMAR%0A5fvfvHrReC%2FuNcjUXo10lPF8Lx4%3D%0A)

Graph has two types of permissions: Delegated and application (see [Power BI permissions](https://learn.microsoft.com/en-us/power-bi/fundamentals/service-get-started?WT.mc_id=Portal-Microsoft_AAD_RegisteredApps)).

In this example we will use delegated permissions.

Add the necessary scopes you will need for the types of operations that you will be doing. Usually, the scopes are listed in the API docs (example: Get Groups [https://learn.microsoft.com/en-us/rest/api/power-bi/groups/get-groups](https://learn.microsoft.com/en-us/rest/api/power-bi/groups/get-groups))

> Note the added scopes for later

Next, you may need to click “Grant admin consent” as an administrator for some or all permissions, i.e. application permissions and high-privilege delegated permissions (see [consent](https://learn.microsoft.com/en-us/azure/active-directory/develop/permissions-consent-overview#consent)).

Return to the overview section and copy the following for later:

-   Application (client) ID
    
-   Directory (tenant) ID
    

Select “Endpoints” in the Overview section and copy the following for later:

-   OAuth 2.0 authorization endpoint (v2) (example: [https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/authorize](https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/authorize)
    
-   OAuth 2.0 token endpoint (v2) (example: [https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token](https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token]\(https://login.microsoftonline.com/33e1b5d3-361d-4859-be90-6f3cd100757f/oauth2/v2.0/token\)))
    

## Finish Creating Power BI Credential in Tines

Return to the credentials screen in Tines we left earlier in the guide.

> Choose either 1a or 1b as your method of authentication

1a. OAuth 2.0 (Authorization Code Grant Type)

-   Paste Application (client) ID in Client ID field
    
-   Paste secret value into Client secret
    
-   Insert a space-separated list of scopes that match with the API permissions you selected in your Azure Application (example below) into Scope:
    
    -   [https://analysis.windows.net/powerbi/api/Dashboard.Execute.All](https://analysis.windows.net/powerbi/api/Dashboard.Execute.All)
        
    -   [https://analysis.windows.net/powerbi/api/Dashboard.Read.All](https://analysis.windows.net/powerbi/api/Dashboard.Read.All)
        
    -   [https://analysis.windows.net/powerbi/api/Dashboard.ReadWrite.All](https://analysis.windows.net/powerbi/api/Dashboard.ReadWrite.All)
        
    -   [https://analysis.windows.net/powerbi/api/Dashboard.Reshare.All](https://analysis.windows.net/powerbi/api/Dashboard.Reshare.All)
        
    -   [https://analysis.windows.net/powerbi/api/Dataset.Read.All](https://analysis.windows.net/powerbi/api/Dataset.Read.All)
        
    -   [https://analysis.windows.net/powerbi/api/Dataset.ReadWrite.All](https://analysis.windows.net/powerbi/api/Dataset.ReadWrite.All)
        
    -   [https://analysis.windows.net/powerbi/api/Workspace.Read.All](https://analysis.windows.net/powerbi/api/Workspace.Read.All)
        
    -   [https://analysis.windows.net/powerbi/api/Workspace.ReadWrite.All](https://analysis.windows.net/powerbi/api/Workspace.ReadWrite.All)
        
-   Set Grant type as Authorization code
    
-   Paste the OAuth authorization endpoint into the OAuth authorization request URL (example: [https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/authorize](https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/authorize))
    
-   Paste the OAuth 2.0 token endpoint into the OAuth token URL (example: [https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token](https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token))
    
-   PKCE challenge method: None
    
-   Paste the following in Domains separated by spaces:
    
    -   \*api.powerbi.com\* \*login.microsoftonline.com\*
        

Optional

-   Set the teams this credential can be used in access and set expiry notifications recipient in expiry notifications.
    
-   Select save.
    

1b. OAuth 2.0 (Client Credentials Grant Type)

-   There are some additional steps outside of Tines for Client Credentials.
    
    -   In the Azure Application, under Expose an API, add an Application ID URI. Select Save.
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973589/dd0115b4a8627e5ca46bfdd3eb30/5c6cd931-5375-49d2-aa32-97cec4a3153f?expires=1784786400&signature=f70847350aee9ef516bfbb3a52e943083c583fae6727827e0aa9cade3c176ce0&req=diMhFsB5noRXUPMW3nq%2BgaaFxiaXg2wGp95NWmtW%2BgKs6s2YuIYaYwjRDJvn%0A1zioRDZmDo%2F97SIwlcABQVLsEm0%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973590/d5021f02893e2d1a583593c225d3/8710457e-ec50-463b-97c5-981bc428d101?expires=1784786400&signature=d85aefaf7f869468758c4834dd92181c77bc8beb787e8a64319e82d4afc162a0&req=diMhFsB5noRWWfMW3nq%2BgfbufwP1lizN1Dc8RrIq9td5oxkNgNgLgRN4pCo4%0Agcz5Jcxt40FXGwJxq0QhITeYaTA%3D%0A)

-   In your PowerBI Workspace (Not “My Workspace”), navigate to “Manage access” and select “Add people or groups”.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973588/ce6c5dad1721c96b0d7e7041a738/788adb4e-32bc-481d-a4c3-456d8924dd83?expires=1784786400&signature=8bddd4be8318bb4109ea4326f1dc79ce5ce863bd27082a6869e8a8111976a17d&req=diMhFsB5noRXUfMW3nq%2BgbZXti2UTYAJ2vlrUnLr4vOz3xczYynIpwxFYG93%0ACOcMHatU6%2Bw6DNJvDEf8OCRIfJM%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973592/37839a802f6d422fe7247f3e13f6/17e53f31-958f-4978-b207-73d861aa0883?expires=1784786400&signature=7358b356117d05a5ae9939b1b080c4dbb63dfffa771435cb31853d392506ffa4&req=diMhFsB5noRWW%2FMW3nq%2BgfFItRAZ5a3lWSAfaDmP8py77MiibTzDokWC%2F8xT%0Ax3tVtaKuGeFumkNVpcywMXmtf%2Bg%3D%0A)

-   Search for the name of your Azure Application (the AppID in PowerBI will match the client ID from Azure). Add the App as an Admin and save.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973587/375d359e90e6eb5620c3e662cb29/da1dda1f-c679-4300-9098-f1d482079b20?expires=1784786400&signature=5c2a77fcd155881f5f966441201c8ff357f11d755176f6a276b63d24832d45fd&req=diMhFsB5noRXXvMW3nq%2BgSvbL9OHnxiSasEv76J5FsyROK9fCSXDRgcMI5cC%0AAACbjh%2FahX1t7Ubczt5cteDIk14%3D%0A)

-   Back in Tines
    
    -   Paste Application (client) ID in Client ID field
        
    -   Paste secret value into Client secret
        
    -   Paste the following into Scope
        
        -   [https://analysis.windows.net/powerbi/api/.default](https://analysis.windows.net/powerbi/api/.default)
            
    -   Set Grant Type as Client credentials
        
    -   Paste the OAuth 2.0 token endpoint into the OAuth token URL (example: [https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token](https://login.microsoftonline.com/your-directory-tenant-id/oauth2/v2.0/token))
        
    -   Set the following for URLs and Domains
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/2360973591/b432972cec62f4f8dcfbcdc40ca1/b17b0e2a-d23f-4d71-9a83-bfb14c03a079?expires=1784786400&signature=2e3b17db6921a77f9813715af70a5810231ab642773a65a911e4ff87c5b298ca&req=diMhFsB5noRWWPMW3nq%2BgSBY%2BNW9Xaot%2FfVjmIL%2FhYvBPawekr59sW%2Bb0cpI%0AZdHLFIkXue9SxNJNiIyraE6B85A%3D%0A)

2\. Save and then authorize Microsoft account.

3\. Test with an HTTP request that is within your permissions (example Get Groups).

```
{"standardLibVersion":"87","actionRuntimeVersion":"71","agents":[{"disabled":false,"name":"Get Groups","description":"Returns a list of workspaces the user has access to.\n\nhttps://learn.microsoft.com/en-us/rest/api/power-bi/groups/get-groups","options":"{\"url\":\"https://api.powerbi.com/v1.0/myorg/groups\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.powerbi>>\",\"Content-Type\":\"application/json\"}}","position":{"x":2415,"y":855},"type":"httpRequest","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:c18f5f5beb294f638984cec56ebf3515:72a1c33baa387d031f0775fdd62e118d"}],"sections":[],"links":[],"diagramNotes":[]}
```

## Preferred AI providers FAQs

## **Is there a specific LLM a Tines builder should consider?**

No - the right LLM for one business might be different from another. Customers can choose to use the one their organization already has in place.

We created [this Tines story](https://www.tines.com/library/stories/1281993/?name=compare-prompts-across-llm-models-in-openrouter) to help you compare prompts across different LLMs and find what works best for you.

## **Does using a different AI provider affect the security of using AI in Tines?**

No. Customers engage with third-party applications at their own risk, as is the current status at Tines. Regardless of which LLM is used, Tines will still handle customer data using our same security and privacy rules.

> For more, see “Topics and common questions” in the [Tines Trust Center](https://trust.tines.com/) or this blog on [keeping a security mindset with AI](https://tines.com/blog/securely-bringing-your-own-ai-to-tines/).

## **Does using a preferred AI provider bring in any extra costs?**

No extra costs are incurred with Tines specifically. Costs will be associated with the separate AI provider the customer uses.

-   [AI Agent action](https://www.tines.com/docs/actions/types/ai-action/): No longer costs run-time credits as Tines run-time credits are no longer consumed. Tines no longer incurs a cost when the action is triggered – much like [Automatic mode for Event Transform](https://www.tines.com/docs/actions/types/event-transformation/automatic/).
    
-   [Workbench](https://www.tines.com/docs/workbench/) is still a paid add-on, with its cost unaffected by the LLM you choose.
    

> For more on Tines pricing and packaging, read [here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging).

## **Does using a preferred AI provider affect how the different AI in Tines features are used?**

Different LLMs excel at different tasks. For AI features in Tines, we recommend using a model equivalent to the ones Tines supports to experience quality AI outputs. Tenant admins can switch to another available LLM or revert back to the Tines default at any point.

## **How can tenant admins get set up with their own AI model?**

Have the AI API key handy for their Anthropic or OpenAI provider.

Navigate to **AI settings** - **Providers** - **Custom provider** - choose the provider and enter the API key

> Watch a step-by-step [here](https://tines.com/docs/admin/ai/).

## **Can builders bring their own AI provider into a community edition tenant?**

Yes, using a custom AI provider in Tines is available for all tenants.

## Can access to an AI provider be restricted to specific teams?

Yes. Tenant owners can scope any AI provider to specific teams, rather than making it available to all teams by default. This is useful when you want to manage costs, meet compliance requirements, or roll out a new model incrementally.

The team that owns the provider's credential will always have access. All other teams have access by default unless the provider is explicitly scoped. You can configure team access in Settings > AI settings > select the provider > Configure > Access and deselect Grant access to all teams on this tenant" to make changes.

## **Who can builders reach out to with questions or troubleshooting?**

-   Tines builders should utilize the Tines in-product chat or reach out to their account teams with general questions.
    
-   For troubleshooting, builders can start with our Support team; however, they should note that issues may be with their AI provider, not Tines, and should reach out to that vendor.
    
-   For builders with self-hosted tenants, we recommend working with Professional Services for effective setup and understanding.
    

> Bookmark our [Admin-AI docs](https://www.tines.com/docs/admin/ai/) and [Use a preferred AI provider in Tines](https://explained.tines.com/en/articles/10371885-use-a-preferred-ai-provider-in-tines) for all available resources on using a preferred AI provider.

## Public cloud cost automation

Understanding and managing cloud infrastructure costs is a critical challenge for many organizations, who often view these expenses as OpEx (ie. paid for monthly) rather than CapEx. Without a clear understanding of the expense required to run resources in their cloud provider, some customers are forced to consider moving from public cloud to private cloud specifically due to cost concerns.

Tines helps you overcome this challenge by collecting cost metrics and information and displaying it in a meaningful manner.

[Watch video](https://fast.wistia.net/embed/iframe/r3763bem4e)

## Unifying cloud cost metrics

The Tines platform can be used to gather and analyze infrastructure cost data across your environment. Utilizing tools like [Workbench for Storyboard](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot), Tines collects key information geared toward showing how much a system or service is costing your organization.

We can collect data for various resources, including:

-   **Compute:** EC2 instances, and Kubernetes environments such as EKS, GKE, and AKS.
    
-   **Storage:** EBS volumes and snapshots.
    
-   **Networking:** Elastic IP addresses and NAT gateways.
    

All of this data is collected, centralized, and visualized in a single pane of glass. This visualization can provide a complete breakdown of charges, such as costs for EC2 instances versus EKS nodes, an overall count of the resource inventory, and a summary cost report.

> Note: While Tines offers a Tines Page for visualization, this information can also be integrated into another application if preferred.

## Visibility across hybrid environments

Since most organizations operate in hybrid environments, Tines is built to be cloud-agnostic. It can go out to various platforms and applications, including AWS, GCP, Azure, Oracle Cloud, private cloud providers, or on-premise systems to collect the necessary information.

Tines aggregates this complex data at a central point (known as an "implode"), pulling together information from all platforms to provide a cohesive view of your environments.

## Driving actionable cost optimization

Once the data is aggregated, you can use Tines to leverage it for proactive cost management:

-   **Executive reporting:** Roll up the total spend for all environments and distribute it by email to executives.
    
-   **Optimization recommendations:** Pass collected cost metrics to a [Tines AI Agent](https://explained.tines.com/en/articles/11541238-get-started-with-agents), which can help you make decisions for cost savings. These recommendations may include right-sizing or configuration drift analysis using a Tines workflow.
    
-   **Historical analysis:** Store the cost information in [records](https://explained.tines.com/en/articles/7971187-introduction-to-records) to enable month-over-month analysis.

## Pulsedive Authentication Guide

Pulsedive is a threat intelligence platform that combines known community threat intelligence into one place and vets that data to reduce noise and help make determinations. It correlates observed IOCs by ASN or country and more complex characteristics like HTTP headers and PTR records.

### First, get a Pulsedive API key

1.  Login to your Pulsedive account at [https://pulsedive.com/login](https://pulsedive.com/login)
    
2.  From your account menu, click "Account"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852347962/b34a31ed7fcb157cedf0c01d/image.png?expires=1784851200&signature=a5a006f78ebf717dabaa85b212d39ca71eb1fada2094ecaf73bf84a6f7a17301&req=fCUlFc15lIddFb4V1XW4gdTZEqggDHQvZmKVzzgSiXRm74mp6mWHZDsq6rYI%0AhFUqvqPEBMh2zVqGCaJzsKdPew%3D%3D%0A)
    
      
    ​
    
3.  Copy your API key  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852348569/c31548afc6736c5fec099485/image.png?expires=1784851200&signature=fefcb3a8625b1ef83fe7435537f79408164909d56cacd0a35c542471b3c0eefb&req=fCUlFc12mIdWFb4V1XW4gUad2z29bryjHi1dudrNPeRSgsKvHuRYui63%2BJAY%0AwyNh7rvn2xuPohNa44e6Y8dR3w%3D%3D%0A)
    

### Then, create a Pulsedive credential in Tines

**Pulsedive connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Pulsedive" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Pulsedive connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Pulsedive credential
    
    1.  **Name:** Pulsedive
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/pulsedive).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Pulsedive** **stories in the [story library](https://www.tines.com/library?s=pulsedive&view=all)**

  
​

## Qualys Authentication Guide

The Qualys Cloud Platform gives you a continuous, always-on assessment of your global IT, security, and compliance posture, with 2-second visibility across all your IT assets, wherever they reside. And with automated, built-in threat prioritization, patching and other response capabilities, it’s a complete, end-to-end security solution.

Authentication with Qualys is performed via ‘Basic Authentication’. With this method of authenticating, valid Qualys user account credentials are required to make API requests to the Qualys API servers. These servers are hosted at the Qualys platform, also referred to as the Security Operations Center (SOC), where your account is located.

> **Important note:**  
> Qualys user accounts that have been enabled with VIP two-factor authentication can be used with the Qualys API. However, two-factor authentication is not used when making API requests. Two-factor authentication is only supported when logging into the Qualys product.

### Create a Resource and Credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Qualys resource
    
    -   **Name:** Qualys\_user
        
    -   **Description:** Optional
        
    -   **Builder:** Your Qualys username
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    
7.  Navigate to the team that will be using the API and click "Credential"
    
8.  Click "+ New Credential" and select "Text"
    
9.  Input the values for the Qualys credential
    
    -   **Name:** Qualys
        
    -   **Description:** Optional
        
    -   **Value:** Your Qualys password
        
10.  **Optional**
     
     -   **Domains:** Ensure that this credential can only be used when making HTTP requests to specific domains
         
     -   **Access:** What other teams can also use the credential
         
11.  Click "Save"
     

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Qualys stories in the [story library](https://www.tines.com/library?s=qualys&view=all).**

### Using the credential in an action

Here is an example Qualys action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"List Reports in Qualys","description":"List Reports in Qualys","options":"{\"url\":\"https://<<RESOURCE.qualys>>/api/2.0/fo/report/\",\"method\":\"get\",\"payload\":{\"action\":\"list\"},\"headers\":{\"X-Requested-With\":\"Tines\"},\"basic_auth\":[\"<<RESOURCE.qualys_user>>\",\"<<CREDENTIAL.qualys>>\"]}","position":{"x":1455,"y":1515},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"d10e9805682e7786d922352f9d00077127037a8ca762627c20b8a33201a311cd","createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```

## Recorded Future Authentication Guide

Recorded Future empowers organizations to reveal unknown threats before they impact business and enables teams to respond to alerts 10 times faster. To supercharge the efforts of security teams, their technology automatically collects and analyzes intelligence from technical, open web, and dark web sources and aggregates customer-proprietary data.

## First, get a Recorded Future API key

If you have already acquired your API token for the Tines Integration with Recorded Future, please follow the instructions below. If you do not currently have access to an API Token for the Tines Integration and are an existing Recorded Future customer please contact your Account Manager to obtain your token.

## Lastly, create a Recorded Future credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Recorded Future credential
    
    1.  **Name:** Recorded Future
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Recorded Future** **stories in the [story library](https://www.tines.com/library?s=recorded%20future&view=all)**

## Records FAQs

## How many record types can I have on my Tines tenant?

You can have 50 types of records per team in Tines.

## Is there a limit to the number of characters in a record?

Yes. The limit for the text field in a record is 15,000 characters.

> To view other limits within records, read our [docs](https://www.tines.com/docs/records/limits/).

## Referencing Credentials

This guide explains the different ways to reference credentials and find their IDs within your Tines environment.

## Referencing an existing credential in a story

When building a story, you can directly reference an existing credential in a story using `CREDENTIAL.your_credential_name` in the builder, as shown below.

Example:

`CREDENTIAL.slack_tines`

![](https://downloads.intercomcdn.com/i/o/am0g137l/2107134501/cc482a98d912749295d2cdc7d256/3da72adf-b38a-4a8a-b43d-7d45daa2e132?expires=1784851200&signature=938402cb35610d7cddcf1ab6d2733f6b8afa43607c2a0d7386a6a2e70b8c6335&req=diEnEch9mYRfWPMW3nq%2BgdfekUznYYHX56kiUiS3ZuzDU2DTHgw0zlvrlvVv%0AVcwtebzv4zTqiFPPDY%2FhjE0NlVI%3D%0A)

You can also use the credentials List API to retrieve a list of credentials.

To learn more about the Credentials API including examples [see here](https://www.tines.com/api/credentials/list).

## Referencing a credential created in a previous action

To reference a credential you created in a previous action, use the `GET()` function to get the credential.

Example:

`GET(CREDENTIAL, previous_action.body.name)`

### Handling name formatting:

If the name of the credential from the previous action is not already in snake case, you may need to wrap the name path in the `TO_SNAKE_CASE()` function.

Example:

`GET(CREDENTIAL, TO_SNAKE_CASE(previous_action.body.name))`

To learn more about using the `GET()` function, click [here](https://www.tines.com/docs/formulas/functions/get/).

## Finding the ID of a credential

The unique ID for a credential can be found directly in your browser's address bar when viewing the item in your Tines team's console.

1.  Navigate to Credentials in the relevant team.
    
2.  Select the specific credential.
    
3.  The ID is the segment of the URL that follows /credentials/.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2107145287/73251265f26b7fbcd01af0020e4e/b71c20c6-d42b-4227-aaab-b6431e68de61?expires=1784851200&signature=1c0d02d3bed4f22be87427cd836629d0f6b94345ada28eeb574dbf21f83a9342&req=diEnEch6mINXXvMW3nq%2BgVvrK0UMcwPl6Q1oRLkW4zSMGW997ja3hGrFsGrb%0AnO7olyshvGdiWTnuzsVcUwZlbNo%3D%0A)

## Removing team members

You can remove team members from a team by clicking on your team name in the top left and navigating to your settings. From here, click "Users". Click the three dots beside the name of the user you wish to delete.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810239922/444407bcd9d2e2056636044e3cd4/CleanShot+2025-11-03+at+10_48_02%402x.png?expires=1784851200&signature=41dde9744c93cbb0bd7810546e953c406bcc874f22384959eefac4fa3674a00f&req=dSgmFst9lIhdW%2FMW3nq%2BgZ161OLXtVCrFCQkEgMdlZUBE%2Fo0qkKQwD%2F66%2BzO%0ALuumdZ8RgZ%2BfuVQYbFXvvJE6cyE%3D%0A)

> **Note:** You cannot undo this action. The user will be deactivated immediately and enqueued for deletion.

### **When a user is deleted in Tines, what happens to their stories, credentials, and resources?**

When a user is deleted in Tines, the Stories, credentials, and resources saved in a team will remain available.

The deleted user's drafts will be deleted. Any shared credentials in those drafts will also be deleted.

### **What happens to a Tines API key when the user account that created it leaves the tenant?**

When a user creates a Tines API key, there are several levels of access they can set during creation:

-   **Tenant owner:** Access to the entire tenant
    
-   **Team:** Role-based access to a specific team on the tenant
    
-   **Service:** Access to [tenant permissions](https://www.tines.com/docs/admin/user-administration/#tenant-permissions)
    
-   **Personal:** A key tied to your identity and permissions
    

> **Note:** The level of access the user can select at the time of the API key creation also depends on the user's permissions within the [tenant](https://www.tines.com/docs/admin/user-administration/#permissions)/[team](https://www.tines.com/docs/admin/teams/).

Let's look at an example. We have a user named Jane Tino, who, within their tenant, is a tenant owner and also a [team admin](https://www.tines.com/docs/admin/teams/#_1-team-admin) of "Team Automation". This would give Jane access to all four access levels mentioned above.

Within Team Automation, Jane creates Tines API keys for each level as [credentials](https://www.tines.com/docs/credentials/).

-   If Jane's account is active in the tenant, all four API keys will work as intended.
    
-   If Jane's account exists in the tenant but is deactivated, the *Personal API key* will break (returns a `401` status code), while the other three will still work as expected. If Jane's account is re-activated, the Personal API key will continue working again.
    
-   If Jane's account is completely deleted from the tenant, the *Personal API key* will break (returns a `401` status code), while the other three will still work as expected.
    
-   If Jane is re-invited back to the tenant, the *Personal API key* will not work (returns a `401` status code), while the other three will still work as expected.
    

In summary, when a user is deactivated or removed entirely from the tenant, the only API key that stops working is their Personal API key. Even if they are re-invited to the tenant, the Personal API key from before will still not work.

## Resources in Tines

Resources provide a way to store information in a single, centralized location that can then be accessed from any action within Tines. This eliminates the need to update the same information in multiple places when changes occur. Think of resources as your automation's shared knowledge base—centralized information that can be accessed across your entire Tines environment.

Tines supports:

-   Plaintext resources
    
-   JSON objects (such as arrays)
    
-   Files (with a 5MB size limit)
    

### How to create resources

Resources can be created in two ways:

1.  Selecting "Resources" in the menu opened by clicking your team name in the top left.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1807175220/e05eac8f5bb6169b0ac1886aa220/CleanShot%2B2025-10-31%2Bat%2B17_59_45-402x.png?expires=1784851200&signature=c1aced1d9278dd697d656f77f71ec1351973a710d40e8688a88e16efed66b6cf&req=dSgnEch5mINdWfMW3nq%2BgShfTVakf2nknw%2FExjYW4iiQr6jFMUQ9CsGKNAfa%0A1JG6oP78zmyhP3MkUAyOMtv1cYQ%3D%0A)
    
2.  From the storyboard by selecting the "+" next to the word Resources in the right-hand panel.
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1807179140/6c01736b15ffc2d7398e04b7139d/CleanShot%2B2025-10-31%2Bat%2B18_02_49-402x.png?expires=1784851200&signature=c45ee3a91e2f22817a9450538ddcafa480a34008069b7dfe98965e596a9489ed&req=dSgnEch5lIBbWfMW3nq%2BgbjNCSGSP7UVCYiC%2BoaGKSF3eEVeqdq4kOrmYq8r%0AokokjVMh%2BcpRuvCUx1fnRFB100A%3D%0A)
    
    To use a resource in an action, you reference it with the `RESOURCE` key in formulas. When the action runs, the resource placeholder is replaced by the contents stored in the referenced resource.
    

> **Note:** If a resource is renamed, its name will change in any of the stories it's used in.

### Resource sharing

Resources have configurable access controls:

-   By default, resources are only accessible to the team they are created within.
    
-   The team that owns the resource will always have access to it.
    
-   Resources can be shared with all teams in the tenant, including personal teams, by selecting the 'All teams' access option.
    
-   Resources can be shared with specific teams by selecting the desired team names.
    

When resources with the same name are shared across multiple teams, a story will use the resource located within the same team as the story.

> **Note:** When a user is deleted in Tines, the resources they created and saved in a team will remain available.

#### Finding the ID of a resource

To find the ID of a resource, navigate to "Resources" in the relevant team, and select the resource.

The ID of the resource is shown in the address bar after `/resources/`

![](https://downloads.intercomcdn.com/i/o/am0g137l/1803232577/43feb1309f3f760d1d99de26f691/image.png?expires=1784851200&signature=e486f5358b3a41d65a1e4f464458bfd9b734ad1d445f4dc8f09396853f03f9a7&req=dSgnFct9n4RYXvMW3nq%2BgWm5GbnhfzE1AQJP0Gmen7WsoWqP9NOUPFAkyh5k%0AOKv12eMxTTYsRO0QVy%2FDl4NOEQc%3D%0A)

## Duplicating a resource

Duplicating a resource within a team makes it easier to create variations of existing resources without starting from scratch.

To duplicate a resource, click on the three-dot Actions button beside the resource and click on ‘Duplicate’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2061498107/6259f64e99325d3f3075d99dcdc6/9636405b-bc6b-48a7-b7c4-a12b45cf311e?expires=1784851200&signature=b6d84a4a202f0c5024a3458c29e76244cca51eca269942999c31117f760cd599&req=diAhF813lYBfXvMW3nq%2Bge8uzhdZB7vp7%2ByhkcJxx%2BU%2BqHnepF0io7n7kaya%0ACoMW46EcgnDSLti%2FXbOMkjddhMg%3D%0A)

## Roles in Tines

All members of a team or group must be assigned a role in Tines. 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.

## **How do I invite users to a team?**

Adding members to a team is simple:

1.  Open Tines and click on your team name
    
2.  Click `Settings` and then `Users`
    
3.  Hit `+New` and enter the email address of the user you want to invite. You can separate multiple email addresses with a comma.
    
4.  Optional: Assign teams and roles
    

After a team member accepts the invitation, you can configure their role.

[Embedded content](https://demo.arcade.software/UF8NhjRURbTuThvTbQlG?embed)

## Roles

These are the roles available in Tines:

-   Team Admin
    
-   Editor
    
-   Viewer
    
-   Case manager
    
-   Workbench User
    
-   Dashboard Manager
    
-   Dashboard Viewer
    

> Note: A user can only have one role. Roles cannot be combined to grant additional permissions.

### Team Admin

This role gives the user unrestricted read and write permissions to all objects in Tines.

Team admins can:

-   Read stories, resources, credentials, events
    
-   Read cases
    
-   Create / update stories, resources, credentials, cases, records & dashboards
    
-   Run an action
    
-   Import / export stories
    
-   Publish / push stories
    
-   Manage team members (add / remove)
    
-   Share, move & delete objects (stories, resources, credentials)
    
-   Author Workbench presets
    
-   Use Workbench presets
    
-   Configure dashboards
    
-   Edit dashboard layouts and charts
    
-   Manage dashboards
    

### Editor

This role gives the user read and write permissions to most objects in Tines, but unlike the team admin role, they cannot delete stories, resources or credentials.

Editors can:

-   Read stories, resources, credentials & events
    
-   Manage cases
    
-   Create / update stories, resources, credentials, cases, records & dashboards
    
-   Run an action
    
-   Import / export stories
    
-   Publish / push stories
    
-   Author Workbench presets
    
-   Use Workbench presets
    
-   Manage dashboards
    

### Viewer

This role gives the user read-only permission to most objects in Tines.

Viewers can:

-   Read stories, resources, credentials & events
    
-   Read cases
    

### Case manager

> Note: This role is only available to customers with cases enabled.

This role gives the user read and write permissions to cases, while restricting access to other objects on the team such as stories, resources, credentials and events.

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.

Case managers can:

-   Read cases
    
-   Create / update cases
    
-   Create / update dashboards
    

### Workbench User

This role grants access to use configured Workbench 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.

Workbench users can:

-   Use Workbench presets
    

> Note: Workbench users cannot access any other part of Tines in that team.

### Dashboard Manager

> Note: The Dashboard Manager and Dashboard Viewer roles are only available to customers with cases enabled.

This roles grants access to view and configure dashboards, including making changes to layouts and charts

Dashboard managers can:

-   Configure dashboards
    
-   Edit dashboard layouts and charts
    
-   View dashboards
    

### Dashboard Viewer

This role grants access to view dashboards without the ability to make changes

Dashboard Viewers can:

-   View dashboards
    

### Custom roles

[Embedded content](https://demo.arcade.software/UY8nEVk55IoEKFXTO4VT?embed)

If you are looking for information on creating custom roles, click [here](https://www.tines.com/docs/admin/user-administration/custom-roles/).

## Schedules in Tines

A Tines schedule is a way to set a workflow or action to run automatically at a specific time or at a recurring interval.

### How do I schedule my story?

Only stories **published to a team** can be scheduled. To publish a story, move the story from your drafts to one of your teams.

To schedule a story, click on the first action and click "Status". The schedule option will be available as shown below.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806366831/a919326741f148e5ba86e6e3d869/Screenshot%2B2023-04-28%2Bat%2B11_59_17.png?expires=1784851200&signature=37279202496f68555dc9cd4bcfc08856cfe579a2eedd6fda21b8463dd3a13bca&req=dSgnEMp4m4lcWPMW3nq%2BgQWC46KKN9EIe6L7iaMqGYiorTxbBJuDBWA53Lj4%0AiLfD3kjUVIxAj3Bhvi5gBWPbZHk%3D%0A)

> **Note:** Actions inside a [group](https://www.tines.com/docs/stories/groups) cannot be scheduled. Groups can be scheduled to run.

### How can I schedule an action in Tines?

You can schedule the following actions in Tines.

-   HTTP Request
    
-   Send Email
    
-   Event Transform
    
-   Send to Story
    

> **Note:** To use them to schedule your story, ensure the scheduled action is the first action in the story flow.

To schedule an action, click the action and then "Status" and add the schedule as shown below.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806367160/bcb38d9b7de84eb5943eb8efc86b/Screenshot%2B2023-09-25%2Bat%2B10_47_46.png?expires=1784851200&signature=bd8370eee460f7a0bf9fbc8cfee9928d256de9fff9d216d5e80b7f22a5655f75&req=dSgnEMp4moBZWfMW3nq%2BgZD63nB0d%2BT1E%2FSPgNsoWT4D%2F8i7OGcpPddsZKge%0AmR9G7vXE5NukUHzn8kibCzq8Q5g%3D%0A)

You can schedule the action by the minute, hour, day, week, month, or a cron expression.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806367161/fd2740f0a1d29a9d05a65db4ed32/Screenshot%2B2023-09-25%2Bat%2B10_52_51.png?expires=1784851200&signature=a0bf614a9a1b8cac0e5f950b78b1e0dbc7e680e7ab9e089cc34f3e4db29023bd&req=dSgnEMp4moBZWPMW3nq%2BgZkAkjPRmV4yu3PsiTbght36DJAMiAyFP6QB4r3l%0ABOPO6picHSHsdczpQ%2FMUqfLLAtI%3D%0A)

> **👉 To learn more about using actions in stories, [see here](https://www.tines.com/docs/actions).**

### Schedule limitations

The shortest frequency for [scheduling an action](https://www.tines.com/docs/actions/configuration#common-config) is one minute. The longest frequency (besides setting up a cron schedule) is once a month:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806369466/6891a6678bebc5fbd5736a1ec238/image.png?expires=1784851200&signature=216bc68d66105d22653f65c0ee2dee16783825e7bbfd648c5031adb22d123715&req=dSgnEMp4lIVZX%2FMW3nq%2BgaF0OqDs3KzQgcmwkK56JuFcoT82Aa5ZLiagt%2Fds%0A%2BDuBQJENN4SGbA9e70HOMGGt9ec%3D%0A)

> **Note:** Each story run is independent and should not impact other story runs. If you are scheduling your actions on a short frequency, we recommend reducing your story event retention (via **Keep events and logs**) to avoid hitting any potential tenant event limits.

## Script error: "The script payload is too large to be processed"

## Overview

When building out Python scripts in Tines, you may run into the following error message after executing the script action:

```
The script payload is too large to be processed. - Request must be smaller than 6291456 bytes for the InvokeFunction operation 
```

## Cause and solution

This error is caused by a platform limitation with AWS Lambda, the service we use to execute Python scripts that are built within your stories. The maximum size allowed for the invocation payload is 6MB.

> 🟢 **Solution:** We recommend reducing the size of your Python script, for example, by splitting it out into multiple actions.

## Need further assistance?

Here are resources to help better understand the behavior surrounding the limitation:

-   Tines: [Using Python in Tines: a quick how-to guide](https://www.tines.com/blog/python-tines-how-to-guide)
    
-   AWS: [Lambda quotas](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html#function-configuration-deployment-and-execution)

## Security & privacy for AI in Tines

All AI in Tines is powered by large language models, running on our infrastructure.

By running the models in this way, we have avoided new risks related to data transport, storage, or sub-processing.

## **Language models**

Language models (used by [Automatic mode](https://www.tines.com/docs/actions/types/event-transformation/automatic), the [AI Agent action](https://www.tines.com/docs/actions/types/ai-action/), and [Workbench](https://explained.tines.com/en/collections/10398899-workbench)) run directly in our infrastructure provider, AWS. Language model authors (like Anthropic or Meta) do not have access to or visibility of the running model in AWS, and Tines does not maintain a direct relationship with these entities.

AWS does not perform any training based on prompt data or usage metadata, nor does it log any input/output model data.

## Private and secure by design

Because the language model runs within Tines’ infrastructure, we achieve a very high standard of privacy and security:

| ✓ Stateless | x No public networking |
| --- | --- |
| ✓ Private | x No training |
| ✓ In-region | x No storage |
| ✓ Tenant-scoped | x No queries or output logging |

## Security Scorecard Authentication Guide

SecurityScorecard uses data from publicly available commercial and open-source feeds across the internet to get an “outside-in, hacker perspective of a company’s cybersecurity posture.” A score is a moment-in-time capture of the security profile of a company.

### First, get a SecurityScorecard API key

1.  Login to your SecurityScorecard account at [https://platform.securityscorecard.io/#/start](https://platform.securityscorecard.io/#/start)
    
2.  Click on the menu in the top right and select "My Settings"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848700691/f9e3d551ac79d5406ae52e94/image.png?expires=1784851200&signature=1c0a7f68bdce3aa85db5282605301b991f0c3d6d752265231e63b7a8de223a27&req=fCQvEcl%2Bm4heFb4V1XW4gdCRa9dJSnf7DZDJYt0LJWzDnjd6I9PuRbvrG%2BkY%0A2jdxrdpcVVXsjdN1n7%2BzKPQ06A%3D%3D%0A)
    
      
    ​
    
3.  Select "API" and then click on "Generate New API Token"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848702080/f65466ac148cc995b6ca82b1/image.png?expires=1784851200&signature=8282ec9f16bc3f7f46a2f27ed222e95f820392d67671c9d1290ccfc90b32d5d3&req=fCQvEcl8nYlfFb4V1XW4gR4NbC1eZnQDi5%2FnoOi0ZpDEgeMBlTfn%2Bs9FjHdy%0A8KQh455K5DHX%2Fg0YIddgXJKRwQ%3D%3D%0A)
    
      
    ​
    
4.  Read the warning message and click "Confirm"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848702901/b67182b0d6532b79f717c0de/image.png?expires=1784851200&signature=cfb62a1de3edb2e73b9d1955e1c33c4ee4986f6f499f97d495fd0a7d0f5b6d61&req=fCQvEcl8lIFeFb4V1XW4geoglAhGTMIHS4nzMpH%2B3jP%2Bnp1QZscHXRoGUQKA%0AbhcE9%2FwrDUqDZHFeHVpAGy%2BbJg%3D%3D%0A)
    
      
    ​
    
5.  Copy your API token  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/848704156/d98e1e5488259083cfb49d58/image.png?expires=1784851200&signature=eb719f58832622c10aaab69b4b6a3515d08bd4b3cf2e04c128a8577be1d12d25&req=fCQvEcl6nIRZFb4V1XW4gciBIRC5Tf5Ah0eeVP1x95tbIIwMIoaZ8nWhMP7r%0AlAJsgUACie%2BjzEeJOCpbEpivZw%3D%3D%0A)
    

### Lastly, create a SecurityScorecard credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the SecurityScorecard credential
    
    1.  **Name:** SecurityScorecard
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of SecurityScorecard stories in the [story library](https://www.tines.com/library?s=securityscorecard&view=all)**

## Send to story

Teams regularly need to perform a task or a set of tasks in multiple 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 de-provision story and a vulnerability management story may require the creation of a Jira ticket.

Rather than creating the same set of actions in multiple stories (thus violating the [DRY-principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)), **send to story** allows users to create "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.

> Learn about send to story and Workbench [here](https://explained.tines.com/en/articles/9855926-using-workbench).

## Sub-stories

Sub-stories work the exact same as normal Tines stories. The only difference is that a sub-story has an entry action and an exit action. The entry action must be a [webhook type](https://intercom.help/tines-9c531f867f99/en/articles/6872951-the-webhook-action-type-in-tines) action and the exit action must be a message-only mode event transformation action.

### Enabling a story for send to story (creating a sub-story)

From a storyboard, when no actions are selected, in the properties panel there is a checkbox to enable a story for send to story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2233149261/b641c9b090abfbb1611b0e649f28/enable+send+to+story.gif?expires=1784851200&signature=59b9ad0e8f1066b1421631c325246d5c11aac1cf705a78f4bc9b220a544bf736&req=diIkFch6lINZWPMW3nq%2BgfK5gCdfTQVhpEuSsMUEsfnBHuT%2BouXZHe%2BIlm0c%0A0l8euali1xznOnJ4yBwckvUqEiU%3D%0A)

When this checkbox is clicked, you'll be asked to specify entry and exit actions. A sub-story can only have one entry and one exit action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2233149037/e8ddb857f433b103e72bd65393f8/image.png?expires=1784851200&signature=4d1474d38cd2922359dccf3264c1f444d0c4a7eaf897288cf6b5bf67c7f61b56&req=diIkFch6lIFcXvMW3nq%2BgYabuUW2gChIcCAMd8aCvS7gd2xtEO0eMKHwDTDX%0AtMesS8m4US0IHXh1j2tDCSpzoYo%3D%0A)

### Entry action

When an event is sent to a sub-story, the entry action will emit an event to its receiver actions.

> Notes:
>
> 1.  A send to story can have only one entry point.
>     
> 2.  The entry point for a send to story must be a webhook action.

### Exit actions

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

It is best practice to have a single exit action. Read our "one in, one out" best practice guide for send to stories [here](https://explained.tines.com/en/articles/14035443-send-to-story-best-practice-one-in-one-out).

## Access

Control who is allowed to send to this story. Either select everyone to allow any team to send to this story, or select the team that owns the story to restrict access.

To do this, click on the send to story settings in the right panel.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2233374998/c8c4be24806243c59d155fec34c3/share+access+to+send+to+story.gif?expires=1784851200&signature=bddddfde20bc31a1c51760c6be01d9c4bf3a1e370029beaad8d9902517ebdd7a&req=diIkFcp5mYhWUfMW3nq%2Bgax8fHNVNlOSbyqPvMH8RvBoufmQpzXiQq5QSDUl%0ApkSLzdhOwS8mxYvnZSMZDqngDcI%3D%0A)

> Note: You do not need to share credentials inside the send to story with another team. The other team can send an event to the send to story and receive an event back when done.

## Sending to a sub-story

When you need to send data to a sub-story, you should use a send to story action with the `STORY` key in formulas. For example, say we have a sub-story called `substory` we would send events to this sub-story with the following send to story action:

```
{
  "story": "<<STORY.substory_name>>",
  "payload": {
    "url": "http://example.com"
  }
}
```

The entry action in `substory` will then emit an event similar to the below:

```
{
  "receive_url_to_analyze": {
    "url": "http://example.com",
    "#event_id": "1029",
    "#action_id": "21"
  }
}
```

When this event has run down the story, an event will be emitted at the calling send to story action that matches the exit action's configuration.

For example, let's say the HTTP request action above was named "analyze URL" and we have the following exit action defined in `substory`:

```
{
  "mode": "message_only",
  "uppercase_url": "<<UPCASE(receive_url_to_analyze.url)>>"
}
```

When the sub-story is complete `analyze URL` will emit an event similar to the below:

```
{
  "analyze_url": {
    "uppercase_url": "HTTP://EXAMPLE.COM"
  }
}
```

## Send to Story Best Practice: “One In, One Out”

## What is "one in, one out"?

One in, one out is a critical design best practice for building send to stories. It ensures consistency and prevents unexpected multiplication of subsequent actions in your stories. For send to stories, this principle mandates that for every single event that enters the story, only one single event should exit.

For more information on the send to story action, click [here](https://explained.tines.com/en/articles/6870857-send-to-story).

## How to ensure "one in, one out”

To adhere to the "one in, one out" principle, you must merge all data back into a single event before the exit action.

The correct technical approach for branching and merging is:

1.  **Use conditions:** Use the condition action type at the top of each parallel branch to route the event correctly.
    
2.  **Include conditional bypass:** Implement a conditional bypass (like a "no match") for branches that do not execute.
    
3.  **Use implode action:** Feed the results of all branches and any conditional bypass paths into an **event transform (implode)** action.
    
    -   The implode action should be configured to expect the total number of branches in your story to ensure it waits for all paths to complete.
        
4.  **Format and exit:** Route the single, consolidated output from the implode action to the final exit action.
    

This ensures that regardless of which branches ran, one consolidated event consistently exits the send to story.

> Here is an example story you can import to your own tenant to help you understand:
>
> [send-to-story-example-one-in-one-out.json](https://downloads.intercomcdn.com/i/o/am0g137l/2151438005/db31b6479407e58e27c58f092532/send-to-story-example-one-in-one-out.json?expires=1784851200&signature=ca0430175c5f516358693ac9c7b36f0556ace42d6981f00f9087412938dc1442&req=diEiF819lYFfXPMW3nq%2BgYI78%2BZlabgl%2FjAg30bh6lJCFctGBCrVSo5XBpH3%0ABUs9ROoyuBpJCdwI1rE%2F9eIpoyQ%3D%0A)

If you need a hand importing a story, take a look at this [article](https://explained.tines.com/en/articles/7228854-how-to-import-a-story-in-tines).

## What happens if "one in, one out" isn’t adhered to?

If a send to story processes one incoming event but allows multiple events to reach the exit action (e.g., "one in, three out"), it leads to inconsistencies in the calling story:

-   **Multiplication of actions:** Any subsequent actions in the calling story will run once for every event that exits the send to story. For example, if three events exit, the next action (like an HTTP request to block a user) will run three times.
    
-   **Unexpected behavior:** When you use a send to story action, you expect one event with correlated information back. If you receive multiple, unexpected data streams (e.g. you have multiple exit actions), your flow is compromised, requiring you to account for unnecessary complexity further down the line.
    

Read more about send to story in our docs, [here](https://www.tines.com/docs/stories/send-to-story/).

## ServiceNow Authentication Guide

**ServiceNow** allows employees to work the way they want to, not how software dictates they have to. And customers can get what they need, when they need it

This guide covers 2 authentication options for ServiceNow:

1.  [OAuth](#h_0f8ac5d7b4)
    
2.  [Basic Authentication](#h_f4aeda9a38)
    

## OAuth

You will need to create an OAuth app in ServiceNow.

### Create a ServiceNow credential in Tines

#### **ServiceNow connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "ServiceNow" and follow the prompts to connect, selecting 'OAuth'.​
    

#### Manual credential creation (Client credentials grant)

1.  Login to ServiceNow via [https://developer.servicenow.com/](https://developer.servicenow.com/) and access your instance (e.g. [https://dev123456.service-now.com/](https://dev123456.service-now.com/))
    
2.  Enable the Client Credentials grant type
    
    1.  In the Filter Navigator (top left), type: sys\_properties.list and hit Return.
        
    2.  Select New.
        
    3.  Complete the following fields:
        
        1.  Name: glide.oauth.inbound.client.credential.grant\_type.enabled
            
        2.  Type: true | false
            
        3.  Value: true
            
    4.  Select Submit.
        
3.  Navigate to Filter Navigator > System OAuth > Inbound Integrations > New integration > OAuth - Client credentials grant.
    
4.  Create a new application by completing the following fields:
    
    1.  Name: name of application
        
    2.  OAuth application user: select user
        
    3.  Create auth scope->New auth scope and enter scope name
        
    4.  Auth scope: select scope created in c
        
    5.  Limit authorization to the following APIs: select APIs to limit access to
        
5.  Select Save.
    
6.  In Tines, add a new OAuth2 credential via Credentials > New > Manual creation > OAuth 2.0 > Create credential  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/2259283310/c368f4dd163e185ea427f234f7d3/Manual%2Bcredential.gif?expires=1784851200&signature=f29fde981c582e774b51f2f4784a414bbc562b965db70538cf8ddb3276a3be87&req=diIiH8t2noJeWfMW3nq%2BgZvDqwo4WdEv34VFa6ixUcmqTs8hDZ%2BSyn8BrlcT%0ARHRRDGJtDg73Y2iRKA7WfCcNDZY%3D%0A)
    
    1.  Name: name of credential
        
    2.  Client ID and Client Secret: copy and paste from the application that you just created in ServiceNow
        
    3.  Grant type: Client credentials
        
    4.  OAuth token URL: https://{service\_now\_instance\_name}.service-now.com/oauth\_token.do
        
    5.  Restrict direct access: selected
        
    6.  URLs and Domains: https://{service\_now\_instance\_name}.service-now.com/\*
        
    7.  Metadata: { "domain": "{service\_now\_instance\_name}.service-now.com" }
        

## Basic Authentication

You will need your ServiceNow username and password.

### Create a ServiceNow credential in Tines

#### **ServiceNow connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "ServiceNow" and follow the prompts to connect, selecting 'Basic Auth'
    

#### **Manual credential creation**

This method is not recommended as the ServiceNow connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the ServiceNow credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your password
        
    4.  **Domains:** yourdomain.service-now.com
        
    5.  **Metadata**:
        
        ```
        {
          "domain": "yourdomain.service-now.com",
          "username": "yourusername"
        }
        ```
        
    6.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/servicenow).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of ServiceNow stories in the [story library](https://www.tines.com/story-library?s=servicenow)

## Shodan Authentication Guide

Shodan (Sentient Hyper-Optimised Data Access Network) is a search engine designed to map and gather information about internet-connected devices and systems. Shodan is sometimes referred to as a search engine for the Internet of things (IoT). Applications of the software include market research, vulnerability analysis, and penetration testing, as well as hacking.

## First, get a Shodan API key

1.  Login to your Shodan account
    
2.  Click "Account"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893653176/ce3e59d2c450e944b589dddc/image.png?expires=1784851200&signature=7e2975a9157519c5c5a95e25b1dd0cbac3f7c3f6182d13ac01b7c3a04b79fe56&req=fCkkEMx9nIZZFb4V1XW4gUD5bNzpMXL63GjSHiTotV1NTIsNDuDBWDkp2C13%0A6F%2BwoNK5KTnFy6Gseb45pbbQuQ%3D%3D%0A)
    
      
    ​
    
3.  Copy your API key to a text editor  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/893653699/a6ada991b09b113a3d79b3af/image.png?expires=1784851200&signature=7b186deabdfc08d6f7f4cb204f5ee9fab1e94a15b5c2c09cd93fb187caeb945a&req=fCkkEMx9m4hWFb4V1XW4gfadGE2EIyuaLPqoYCjD3QDPkdQTqNxqQYEc7sk3%0A66c6hl6dSMo1%2F07oYv%2FWP04bXg%3D%3D%0A)
    

## Then, create a Shodan credential in Tines

**Shodan connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Shodan" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Shodan connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Shodan credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    
    1.  **Domains:** api.shodan.io
        
    2.  **Access:** What other teams can also use the API
        

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/shodan).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Shodan** **stories in the [story library](https://www.tines.com/library?s=shodan&view=all).**

## Single sign-on (SSO)

SSO is a scalable way to ensure trusted access to your Tines tenant. As security is always a priority at Tines, SSO is available for all Tines tenants. All Tines users can access Tines via their identity provider.

## Enabling SSO

To turn SSO on or off for your tenant, go to `Authentication Settings` in the top left menu and follow the instructions.

![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/650206958/4d6eaf7b00e025726771274b/1656002378-admin_sso_menu-d516260af68a5a72ac3259233693b699.png?expires=1784851200&signature=25763e2f972c13b514df33eb8c23a50587ebec03ce8f551d15a23475f7cefa02&req=ciUnFMl4lIRXFb4V1XW4gUENIVb7FlOMbIhcNs5zKvljLEl%2FAKawcZsePOl2%0ApGvc2UVbI8b%2B6SDuJGrSiYxOYA%3D%3D%0A)

> **Important note:** SSO only enables users to sign in, not sign up. Users must be invited to gain access.

### 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
    
      
    While we discourage this for security reasons, you can 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)**.
    

#### 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 "email", "openid" and "profile" OAuth scopes.

### 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.

> **Important note:** A change to the just-in-time configuration will not update existing users' teams and roles.

#### To setup just-in-time provisioning:

1.  Enable the checkbox entitled "Enable automatic provisioning of SSO users when first accessing the tenant"
    
2.  Fill in the following blank fields in the json editor:
    

The `group_attribute_name` field should be filled in with the name of the SAML or OIDC attribute containing a user's group name. In the example below the value for this field would be "Group"

![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/701544213/c517fd129593d1a91d841819/1677088092-screen-shot-2023-02-22-at-9-47-27-am.png?expires=1784851200&signature=0bbe1bdc5b5f871dc15eac33c4c7309be45ab58e841fd315e2f72fd087f19f41&req=cyAmE816n4BcFb4V1XW4gV4V9wxAdP2McNLUE%2F1ZhVQ8Zow7fa5d1zOEqh5g%0A%2FROKIhyQqe3c6YycxTCL9ht5Xw%3D%3D%0A)

##### An example of a Group Attribute Statement configuration in Okta.

-   The `tenant_owners_group` (optional). When provided, users in the SSO group matching this field will be provisioned as tenant owners.
    
-   An object in the `mappings` array containing an `sso_group` with a value matching the name of the desired group in your your identity provider, `team_name` with a value matching the name of the Tines team users from this SSO group should be placed into, and `role_name` with a value matching the desired team role for those users (team\_admin, editor, or viewer).

## Single sign-on (SSO)

SSO is a scalable way to ensure trusted access to your Tines tenant. As security is always a priority at Tines, SSO is available for all Tines tenants. All Tines users can access Tines via their identity provider.

> To configure SSO, you must be a **tenant admin** or **owner**. By default, the tenant owner is the person who created the tenant and is the first tenant admin. There can be multiple tenant admins.

### **Enabling SSO**

To turn SSO on or off for your tenant, go to `Authentication Settings` in the top left menu and follow the instructions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810176456/3b18339fbc4630505d4e9bfc1a4b/1656002378-admin_sso_menu-d516260af68a5a72ac3259233693b699.png?expires=1784851200&signature=f28eebb8ad95495b448800090da678fb4e9a5cfd600f07b8bae564ab1b44ff57&req=dSgmFsh5m4VaX%2FMW3nq%2BgXUy7sYtAxhyRpSEyc0q2%2BRK2JbSKbm3E%2BtWJeYm%0AvmN0TXf1h%2FdJltkf2%2F8nUqCsmH0%3D%0A)

> **Note:** SSO only enables users to sign in, not sign up. Users must be invited to gain access.

### **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
    
      
    While we discourage this for security reasons, you can 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)**.
    

### **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 "email", "openid" and "profile" OAuth scopes.

> You can choose the session timeout duration when SSO is configured.

### **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.

**Important note:** A change to the just-in-time configuration will not update existing users' teams and roles.

### **To setup just-in-time provisioning:**

1.  Enable the checkbox entitled "Enable automatic provisioning of SSO users when first accessing the tenant"
    
2.  Fill in the following blank fields in the json editor:
    

The `group_attribute_name` field should be filled in with the name of the SAML or OIDC attribute containing a user's group name. In the example below the value for this field would be "Group"

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810176457/9fd38ff9e6ee7de5eaf95462f086/1677088092-screen-shot-2023-02-22-at-9-47-27-am.png?expires=1784851200&signature=9835bfa47bb636b692a2ce68be515fc971c22093e759c9b9d497221777509a28&req=dSgmFsh5m4VaXvMW3nq%2Bgf4LuIidKNsPeq8xvXynwWRpAT%2BSOgiUIQ5Mdf1b%0A5m086DHML32ZPbEnnJMmydkjjaQ%3D%0A)

### **An example of a Group Attribute Statement configuration in Okta.**

-   The `tenant_owners_group` (optional). When provided, users in the SSO group matching this field will be provisioned as tenant owners.
    
-   An object in the `mappings` array containing an `sso_group` with a value matching the name of the desired group in your your identity provider, `team_name` with a value matching the name of the Tines team users from this SSO group should be placed into, and `role_name` with a value matching the desired team role for those users (team\_admin, editor, or viewer).

## Slack Authentication Guide

## Introduction

Automate workflows across your business with Tines and Slack, allowing you to focus on what matters.

Tines is a workflow platform purpose-built to automate and integrate business processes. Teams are too often stuck doing tedious manual work or force-fitting systems together, and it’s our mission to change that. With Tines, you can build connections between systems in minutes and deliver valuable workflows within hours.

![](https://downloads.intercomcdn.com/i/o/911002706/3b24db7246de3e4abc313d36/Connect+to+slack.png?expires=1784851200&signature=dbe582af5f1942564bd25ff73182d18cceaccdb8fe307bf2ded4abd11c7fb324&req=fSEmFsl8moFZFb4V1XW4gVxdG8n3WZOdn2NXvb0dLmZCNv8fsdU%2BA2RGAWHZ%0A%2FdYMiQurxzaCYB8vRzzBc1669Q%3D%3D%0A)

## Steps for installing the app:

1.  [Login](https://login.tines.com) to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Slack" and follow the prompts to connect.
    

### Workflows you can build with Tines and Slack:

-   Receive and approve or deny requests via Slack
    
-   Create new channels and send messages
    
-   Automate user reminders for training and compliance
    
-   … and a whole lot more! Check out our [library](https://www.tines.com/library/tools/slack?page=1) for even more workflows.
    

![](https://downloads.intercomcdn.com/i/o/911004856/7d3e2ca60fe3cd5f3ed12d2f/Automate+user+messaging.png?expires=1784851200&signature=c23a3f7e386c373ecaa39eacea8fb8948807e44b9f0faacb02fc7469b0b95e68&req=fSEmFsl6lYRZFb4V1XW4gRRtFkG2bhrzZIOFJ2hsWpXHpYfhrhDrEUz62FtM%0AnldGy3U0ZCraSFFiMQlrSnRVVg%3D%3D%0A)

### Get more out of your tech stack with Tines and Slack:

While it’s possible to send simple Slack messages straight from most tools, you can use Tines to build tailored Slack messages that aggregate and correlate data between these systems. This makes your alerts less noisy and more relevant for your team. Tools customers use Tines and Slack to see increased value from include:

-   Cloud providers such as AWS, GCP, and Azure
    
-   Ticketing platforms such as Jira, ServiceNow, and Zendesk
    
-   Security systems such as Wiz, CrowdStrike, Lacework, Recorded Future, JupiterOne, Tenable, Qualys, Elastic, Splunk, and Panther
    
-   Software development tools such as GitHub and GitLab
    
-   And other commonly used software such as OpenAI, Salesforce, Okta, and more!
    

![](https://downloads.intercomcdn.com/i/o/911004935/02c107a06ba15feecd497a37/Build+end+to+end+automations.png?expires=1784851200&signature=bedefac82d6ca68f1e20a306d1e0fcbf5946feb1c4bc7e749db11541b0c67966&req=fSEmFsl6lIJaFb4V1XW4gc5wavreMk17ExnVblPWC4m6HuW8mOk4O%2F5QKn89%0AIrWVJc3uzOoq9zJD%2BObC0k9zlg%3D%3D%0A)

> For more on how third-party data is handled, see Tines's [privacy policy](https://www.tines.com/privacy)

## Custom Slack Authentication Guide:

If you'd like to choose a custom set of scopes, you can create your own app. Follow the steps below:

First, create a chatbot in Slack

1.  Navigate to [https://api.slack.com/apps](https://api.slack.com/apps), select Create New App, select 'From scratch' or 'From an app manifest' based on your preferences, and choose a name. When you create your Slack App, you have to choose your workspace. If this is your first time setting up an application, testing it in a demo or development workspace may make sense.
    
    ![](https://downloads.intercomcdn.com/i/o/754269715/36d69460b9fda6bd2175eaaf/Screenshot+2023-06-01+at+13.10.11.png?expires=1784851200&signature=7f0fae559110831a7da4c40242d33f9ebf3601d010266e8615cdcdef653bfcf3&req=cyUjFM93moBaFb4V1XW4gRsJa5AizolX18ayTL%2BdAIxAC9evjtRnp8WdtEYs%0A9%2FEmf7pRVg%2BrhhTtsMn78pMivw%3D%3D%0A)
    
2.  On the next page, you’ll be presented with several options on what features and functionality you’d like for your application:
    
    ![](https://downloads.intercomcdn.com/i/o/754270262/127f24a8f1b9ea25e6c212a1/Screenshot+2023-06-01+at+13.10.42.png?expires=1784851200&signature=1daea635e607f09d706d441d0c833dd122b2bc5210cb94a7e6a93a88d01ee9d5&req=cyUjFM5%2Bn4ddFb4V1XW4gZIHa2OIOvCLbEAn1duKkq8qyMul5WC3ZOUKhGZn%0Anc1Brwyb9wZwK4rBD%2BSUyQXr0A%3D%3D%0A)
    
    Configure your bot to your desired specifications. Some commonly used features and functionality include:
    
    -   **Permissions:** enable various user and/or bot scopes for your app. Follow [this link](https://api.slack.com/methods) view a full list of actions you can do with your bot. Click into the details for your desired actions to understand what permission scopes are needed. Additionally, all Tines Slack templates list the required scopes for each action in the description of the action template as further guidance.
        
    -   **Interactive Components**: send messages with embedded interactivity to users. For example, send users messages with interactive buttons that can be used to stop and start your automation stories in Tines, upon user input.
        
    -   **Slash Commands**: allow a user to interact with Tines through specific, user-defined commands e.g.
        
        -   /searchdomain – search for traffic to a domain in logs
            
        -   /lookupuser – find user profile information in active directory
            
        -   /quarantinedevice – quarantine a device proactively  
            ​
            
3.  Once you have configured your app to your custom specifications, navigate to the OAuth & Permissions feature tab on the left and locate the section 'OAuth Tokens for Your Workspace.' Depending on the permissions scopes you enabled, you may have a User OAuth token or a Bot User OAuth token. Copy the relevant token to use in the next step.
    
    ![](https://downloads.intercomcdn.com/i/o/885010944/90c1f5921c5a338c9df6f927/slack+token.png?expires=1784851200&signature=63a8f8dfc609f8e591c8739fba66bb47827f11ee6451df3ac656dc2b89c8cb41&req=fCgiFsh%2BlIVbFb4V1XW4gRut2HF%2FrNrAaMJNo2Ch0V%2Fy8hIMx2kOG4KY33kQ%0AyB%2BpcEFp79d9aSZqnG61EznT%2FA%3D%3D%0A)
    

### Create a Slack credential in Tines

**Slack connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Slack"
    
4.  Select "Use my own Slack app" and follow the prompts to connect.
    

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/slack).
>
> For more complex capabilities for your chatbot, check out this [blog](https://www.tines.com/blog/chatbots-for-security-and-it-teams-part-3-creating-a-slack-chatbot)
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Slack stories in the Tines [story library.](https://www.tines.com/story-library?s=slack&page=1)

## Splunk enterprise authentication guide

Splunk is a software platform to search, analyze and visualize the machine-generated data gathered from the websites, applications, sensors, devices, and so on, which make up your IT infrastructure and business.

There are two types of authentication supported by the Splunk Enterprise API:

1.  Access token (recommended)
    
2.  Basic authentication
    

## Create a Splunk enterprise credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Splunk Enterprise" and follow the prompts to connect.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2230883848/b0ddb4437af9719f6b0a47241d66/splunk+add+credential.gif?expires=1784851200&signature=3204433689580cfe281d69d44a3eafc6cb88d3ce1c6670a083aeef5b3bf476ea&req=diIkFsF2nolbUfMW3nq%2BgRQkd4zvuFnkbWq3SVXbz7C4ea3AMwXo3zKfqfnc%0A5j82cuRDrNnqLwKvP6ZQlhsi%2Fcg%3D%0A)

## Configure webhook allow list in Splunk

By default, Splunk blocks outbound communication from Splunk to a Tines webhook.

Follow the steps in [this article](https://help.splunk.com/en/splunk-cloud-platform/administer/admin-manual/10.3.2512/configure-your-splunk-cloud-platform-deployment/configure-webhook-allow-list-using-splunk-web) to add Tines webhook to the allow list.

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/splunk-enterprise).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Splunk** **stories in the [story library](https://www.tines.com/library?s=Splunk&view=all)**

## Staying up to date with Tines

The Tines product team work hard to get new features and products into your tenant. So, you might want to dig into these in more detail from time to time. There are lots of ways to keep up to date with Tines, including:

1.  [What's New feed](#h_01039ede5e)
    
2.  [Tines blog](#h_568951addf)
    
3.  [Newsletter](#h_f0b9ea0091)
    
4.  [Social feeds](#h_d9fc96ab55)
    

### What's New feed

![](https://downloads.intercomcdn.com/i/o/am0g137l/1807094495/156224239173b18b3e16a2acc874/CleanShot+2025-10-31+at+17_11_37%402x.png?expires=1784851200&signature=e4fc8f309f06f7c7edd745c57c9663b157dbd122b4ec9b0cc2ffcfee3be9905e&req=dSgnEcl3mYVWXPMW3nq%2BgcIupfRUX8v9O3FkENp5QSTmLMwCk83qtG9qHfgV%0AzicgqHmzNZe5x8GkO3%2FuhGha%2Fk0%3D%0A)

All Tines feature updates get posted on the **What's New feed [here](https://www.tines.com/whats-new)**. There are two RSS feeds you can subscribe to, depending on your preference.

-   Important updates:
    
    -   [https://www.tines.com/rss/tines-product-updates.xml](https://www.tines.com/rss/tines-product-updates.xml)
        
-   All product updates
    
    -   [https://www.tines.com/rss/tines-product-updates-full.xml](https://www.tines.com/rss/tines-product-updates-full.xml)
        

> **Tip:** Use the filter to the left of the feed to look at specific product categories.

### Tines blog

![](https://downloads.intercomcdn.com/i/o/am0g137l/1807099177/fa87b1f98d495a4dead876b67a3e/CleanShot+2025-10-31+at+17_14_15%402x.png?expires=1784851200&signature=92c67bbfaac75ad1c22cc55443d41324257c71aae7a7fbd43e809637dda92b22&req=dSgnEcl3lIBYXvMW3nq%2BgdY9j2JiMHMnSIU%2FpVe6p0RTyunZMlNcljI6tOA8%0A71Dy15Owp21n7UwMxoJV%2BJcYvEs%3D%0A)

The team posts regularly to the **[Tines blog](https://www.tines.com/blog/)** including relevant news, engineering insights and [product updates](https://www.tines.com/blog/product-updates/). The product marketing team shares a What's New post for updates made that month.

You can subscribe to the blog RSS feed here:

-   [https://www.tines.com/rss/tines-blog.xml](https://www.tines.com/rss/tines-blog.xml)
    

You can catch the Tines quarterly look back video on **YouTube** **[here](https://youtube.com/playlist?list=PLrLy59ZkmoJ6aBiJBdTKPDAWFwinWid6F&si=xAe1fqdVXCaJG82j)**.

### Newsletter

Get Tines news and updates direct to your inbox with the **Tines newsletter**. You'll be automatically added to this list when you sign up, or you can manually sign up on our [blog](https://www.tines.com/blog/). We won't spam you, promise. Just look for this form near the bottom of the page:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1803124864/b9db61eed09a15dbf75dc1c19222/Newsletter+sign+up.jpeg?expires=1784851200&signature=f152fa88538eb78061a6d489e3988f1774ec911fca3858098ac4a38a815a5187&req=dSgnFch8mYlZXfMW3nq%2BgcvVd%2B6rzvSgB5jA545IwuM7B%2BskE7RUYemLGRIu%0AizEf0nxJyh%2FdIu6W65yUw6Fugns%3D%0A)

### Social feeds

You can always see what we're up to and connect with us on the social platform of your choosing:

-   [LinkedIn](https://www.linkedin.com/company/tines-io/posts/?feedView=all)
    
-   [X (formally Twitter)](https://x.com/tines_hq)
    
-   [YouTube](https://www.youtube.com/@TinesHQ)
    
-   [Tines Slack community](https://hq.tines.io/pages/6f8b122ccba3cb7e8e0d3531d1b70eb2/)

## Stories in Tines

Stories are a collection of interconnected actions working towards a singular goal. Think of them like automated use-cases, or playbooks. For example, you might have a Phishing story, an infected endpoint story, or an employee onboarding story.

To begin your first story, simply drag an actions or a template onto your storyboard and connect them like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802430770/65d0941e6f6c7828c88b10a3f159/97f38bbb-56f7-4e30-bdfc-041abfa6995c.gif?expires=1784851200&signature=5fe2605550d839b3f9e0a93e6bd8d8fab9e124287f3304918ced5ed975ded8db&req=dSgnFM19nYZYWfMW3nq%2Bgb0ELO8nIM0tfdjx4g%2Fw9bb7iVrqMkEcDqncKQm1%0AZApLVioYDAsgKbOxQd9tNXoDE7I%3D%0A)

You can also create your first story by importing one from our [Story Library](https://www.tines.com/story-library) from right inside Tines, like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802430772/edff7c91c807f657696429461520/d603a6c9-8924-4f30-b3a9-a5a238c15008.gif?expires=1784851200&signature=a0809f60593b638e89e7b0b736ba92cbd9f4b8bda4e4e69f048906a20b420214&req=dSgnFM19nYZYW%2FMW3nq%2BgTLjGnMGFFm4nMGYv8NEbL6%2BPdjxng36tZKykGay%0A3Ua9hXH2trgGomLTDrtXTK9Wk%2Fg%3D%0A)

Another way to get started with stories is with the default stories available to you when you first create your Tines tenant:

-   **The Simple Story**: A short story that demonstrates how actions and events interact to automate workflows.
    
-   **Simple Story - VPN Notification**: This story demonstrates how automated threat detection and response can be performed on corporate VPN connections.
    
-   **Analyze URL in URLScan**: This story demonstrates how Tines actions can be used to analyze suspicious URLs.
    

So you can better understand what a completed story might look like, here is an example of a story designed to automate threat detection and response of corporate VPN connections:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802430773/b2a223a3bc3e3f7246df19d2f029/1654856057-quickstart_story-7171552b5404f4622813b966a19384a1.png?expires=1784851200&signature=b19bef7630fdc17ed00f61f64f2e0e3039b81b3e4071036bac4a8178efe86dfd&req=dSgnFM19nYZYWvMW3nq%2BgYcXlSCj5IL%2BMZG3o8jWEBA8zGCcDGGyamIqyAeb%0AspkhjS%2FYsLYU1q7BWS0EPpnhJgw%3D%0A)

When you start creating a new story, it is unpublished by default. An unpublished story is similar to a draft. It will be restricted in that it will only run while you're working on it. Unpublished stories do **not** count toward your stories quota.

> **👉 For more detailed docs on how stories work, [see here](https://www.tines.com/docs/stories).**

#### **Backing up your Tines stories**

You can export your stories from Tines, which will be stored in a JSON format.

For more information on exporting stories, [see here](https://explained.tines.com/en/articles/7228646-how-to-export-stories-and-actions-in-tines).

You can also use the Tines API to export stories. For more information on exporting stories using the TInes API, [see here](https://www.tines.com/api/stories/export).

Here are some stories that can be used to back up, export, and import Tines stories.

-   [Backup Tines stories to Github](https://www.tines.com/story-library/87728/backup-tines-stories-to-github)
    
-   [Save Tines story in AWS S3](https://www.tines.com/story-library/87683/save-tines-story-in-aws-s3)
    
-   [Import Tines story from AWS S3](https://www.tines.com/story-library/87682/import-tines-story-from-aws-s3)

## Story outputs and values

## Customize output

The customize output setting allows you to modify the event data that an action produces.

Customize output is available to all action types and can be found via the action configuration → **\+ Option** button:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258017313/544097fb130d6336866d22194fa5/custom+output.gif?expires=1784851200&signature=dcd8c59d998c35952c4aa658027f0b8c0ba5aadfd18903d49d1d68495f3fa249&req=diIiHsl%2FmoJeWvMW3nq%2BgboNUEN%2BGR0ZIAcUyZHq%2FeqcGpW%2FZAn7YfIYOY4y%0AcnzdvBXk%2Bt7QLHZeh7keqUw%2BDXk%3D%0A)

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   Tines: [Actions](https://www.tines.com/docs/actions)
    

### What can customized output be used for?

#### Enhance story readability

Use the customize output setting to format your payload data in one action, reducing the amount of actions in your story. In this example, we want to `MAP` out the "`email`" key from an array of objects.

```
{
  "users": [
    {
      "name": "Angie",
      "email": "angie@tines.com"
    },
    {
      "name": "Shelby",
      "email": "shelby@tines.com"
    },
    {
      "name": "Lasse",
      "email": "lasse@tines.com"
    }
  ]
}
```

Normally, we would have two actions, one for the data and one for formatting it. Using customize output, we can transform this data in one action instead of two:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258016508/46225231b23e0559585d72311b3b/image.png?expires=1784851200&signature=d711448c17a447e1e104da18362ec8a58bfd31a40a4db5a3a5ff3e3c4953af66&req=diIiHsl%2Fm4RfUfMW3nq%2BgT5OJs7HVIbWw4aCVzKWa73GGAlq%2Fev1dSxwwU3a%0AniZ3Z%2F4FzmsxhkpW3kFfrkV6a%2BE%3D%0A)

#### Optimize downstream processing

Use the customize output setting of your HTTP request action to strip the information you don't need. In this example, the Poke API usually returns a lot of information. Using customize output, we can filter the response to only return the data we want:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258016509/d290b8d8dca62930e8f2c882f64f/image.png?expires=1784851200&signature=34132563a61249dc7349f30db8f01949fb9f576effc055189fa950b7fb432d66&req=diIiHsl%2Fm4RfUPMW3nq%2BgRbwTQigp7dTMuNbqRqYhhHBA4SM7b7iRiR1Hc%2B1%0AFbpF0f1OlO6Mx0XX2hWQTqRL%2BaY%3D%0A)

#### Set secret encryption

Use the customize output setting of your action to encrypt and store a token as a Tines credential, hiding the value from event details:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258016507/af25253a15dc5bce0cf5f405d230/image.png?expires=1784851200&signature=a3b914f8511e7028f5169cbe17552f1cd9e75eb8228788f369526ebba3cad0cf&req=diIiHsl%2Fm4RfXvMW3nq%2BgbzZutOyIAWKkoN4OnEX%2FMTi%2Bd%2BmUVknAFGsrzIc%0ANR9ug15KE9J%2FnPz7vIZTY%2B7mink%3D%0A)

### Limitations

The following limitations for the customize output setting are:

-   Input or event data from a different action cannot be included within another action's customize output configuration.
    
-   Key path auto-completion is not available.
    
-   Output preview is not available.
    

## Working with value options

This video explains how to use the different value options available in Tines.

[Watch video](https://www.youtube.com/embed/v5NDAEMPKD0?rel=0)

When working with an event transform action, you can change the different types of values to format or enrich data. You can start by pulling an event transform action into your story.

### Using key-value pairs and adding fields

By default, an event transform action will contain “message : This is an automatically generated message from Tines”, which is a key-value pairing. You can replace this and add your own fields by hitting the plus button to add more objects or key-value pairs.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258957027/755da61d76f9bf8711548850395e/addd+key+value+pairs.gif?expires=1784851200&signature=4239526b8c1f9bde5da8bdffa94c0480c7bbd8d7151f487b7e562e41f9456014&req=diIiHsB7moFdXvMW3nq%2BgRLoD6yVOWGDf2FDsVZ8AdD%2B6mVwUSPQHGWokZNQ%0ACZffRHAOc7%2Fl0zVQPHuNKgfpPug%3D%0A)

### Switching value types

If you do not need all fields for a single action, you can delete them. If you intend to use the action for a single value (such as a number or text), you can click on the value type to see different options. You can switch the value to an array, null, true, false, number, formula, or text.

For example, if you only wanted the message to say "this is an automatically generated message from Tines," you would switch the value type to text. When using single value items, the action will only contain that one item.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258959302/320c552a922c602245232414fa3f/switch+value+types.gif?expires=1784851200&signature=d8f059833bfd71c07e9e8de4daec97ae1d8ff54c89016893fe2a5b4865ca79c9&req=diIiHsB7lIJfW%2FMW3nq%2BgZCjZbue4NfP7IXfOA8UspR0NiLQ%2FHvQYs81Fe2J%0ASVoxq%2BI3ed8J2EZh%2F81HbBjaMVs%3D%0A)

### Add an object

You can also combine different value types. For instance, you could add an object called "data set."

Instead of it being a plain text value, you can click on it to switch it to another object containing your nested key-value pairs.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258962159/3f91a72f3dd776502b0a860f8596/add+an+object.gif?expires=1784851200&signature=243a606388f4a5e9ba7d0be4faebc21a19018c9a5bae4180d9ea90888c47f701&req=diIiHsB4n4BaUPMW3nq%2Bgf1ePeZufwNJSIleYGkEcojokiHfWV6beega84Ow%0A%2FSdBFoSCfD3H42Sso5P7eT1Et4c%3D%0A)

### Add an array

In addition to data sets, you can add an array to create a quick list, such as an "application list".

When adding items to the array by hitting the plus button.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2258964933/dcfa5f9e46c6b32b5358598bb9e1/add+an+array.gif?expires=1784851200&signature=d7288ce1f56371d7bf4d936f861d600d310136e9866ee7ea80509975ff21b7b2&req=diIiHsB4mYhcWvMW3nq%2BgQzg5IpnMgySGk%2BLKC39xuLZcp5W2ONH2LznNU4c%0AY5VvknQnicxVl%2Fvu1I1TbQzRU8w%3D%0A)

## Leverage local values

Using local values within Tines allows you to store the results of calculations to reference within your action payload.

### Defining local values

Local values are available in all [action types](https://www.tines.com/docs/actions#action-types) within Tines. To define a local variable within an action:

1.  Within your Tines tenant, click on the relevant action tile to open up the action's configuration panel.
    
2.  If the **Local values** section is not visible, add it to the configuration via the **\+ Option** button:  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/965474476/e83aa8a3fa7e380a390fb270/Screenshot+2024-02-16+at+11_46_20+AM.png?expires=1784851200&signature=3e14e0bdaacd766fea361958ca369612b705a91f4a973b7858ab898b758863f2&req=fSYiEs56mYZZFb4V1XW4gWeVcelkfKeKPhvqd5BPeYr5mZJCLcTH7607LXEj%0AnPKdxlFQa%2FfPX2hvOVw7dh2wow%3D%3D%0A)
    
3.  Within the **Local values** section, configure the value.
    
4.  Reference the local value in your **Payload** -> **Builder** as `LOCAL.[value]`, as needed.
    

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.

> For examples and more information on local values, visit our [Tines University page](https://www.tines.com/university/builder-expert/advanced-functions/local-values/).

## Story performance and execution

## Check how long it takes a story to run

When you are in your story, click the 3 dot menu in the top right corner. Select **Story Runs** from the menu.

You’ll now see the most recent story runs, the route taken, and any actions executed.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2252504496/938306f7bdc67a10ee7c0d7498bf/story+runs.gif?expires=1784786400&signature=3e75cd7b96f75abc00f2f6d42364e57b876ec6d15035fb64a84c9c9fcb7ba5d5&req=diIiFMx%2BmYVWX%2FMW3nq%2BgW5aui0JoGp86c9nNqOGblKKFkU2qxXJnzL55fyg%0AXw8pBbxmf0TWa0g%2F5sD%2FNpGO2nU%3D%0A)

In this view, you’ll also see the number of events, start time, finish time and story run duration.

> Read more about story runs in our docs [here](https://www.tines.com/docs/stories/story-runs/).

## Action run orchestration

In Tines, it's important to consider the issue of fairness regarding action runs. How do you ensure a single story doesn't monopolize all of the compute power for action runs on a stack, thus stunting other stories? This question applies to single and multi-tenant stacks, where all of the stories share action run computing capacity.

To address this issue, we have a concept called "fair orchestration." "Fair orchestration" guarantees that each story on a stack receives a fair amount of worker time to process action runs.

### How does it work?

The system uses two mechanisms to ensure fair resource distribution: concurrent run limits and token buckets. These are checked in sequence for every action run.

### Concurrent run limits

The system allocates a percentage of total Sidekiq workers for concurrent action runs per story:

-   Multi-tenant stacks: 30% of total workers
    
-   Single-tenant stacks: 50% of total workers
    

This allocation prevents any single story from monopolizing the available workers. The system tracks the number of currently running actions for each story in Redis, and only allows new runs if they won't exceed these limits.

### Token bucket system

If concurrent capacity is available, the system then checks token availability. Each story has a token bucket that refills proportionally based on the time elapsed since its last update, up to a maximum capacity of 1.5 million tokens. These 1.5 million tokens represent 25 minutes worth of worker time per minute. For high-priority stories in single-tenant environments, this limit is doubled to 3 million tokens (50 minutes of worker time).

> **Notes:**
>
> -   1 token = 1 millisecond of worker time
>     
> -   1,000 tokens = 1 second of worker time
>     
> -   1.5 million tokens = 25 minutes of total worker time
>     
> -   High-priority stories (single-tenant only) get 3 million tokens = 50 minutes of worker time
>     
> -   Worker time refers to Sidekiq workers processing capacity the actual CPU time used to execute action runs

As actions in a story execute, tokens are deducted from the story’s token bucket. From the time an action run starts, every second 1,000 tokens are deducted from the story's bucket until the action run is complete. This token consumption is tracked through a dedicated background thread to ensure accurate accounting of active runs.

If a story depletes its tokens, new action runs for that story will be pending until sufficient tokens have accumulated through the time-based refill system.

For example:

-   Let's say a system has 100 total Sidekiq workers:
    
    -   For multi-tenant stacks: 30% allocation = 30 maximum concurrent workers
        
    -   For non-multi-tenant stacks: 50% allocation = 50 maximum concurrent workers
        
-   A story starts with 1.5 million tokens and attempts to run 8 actions simultaneously:
    
    -   1\. First, the concurrent capacity check:
        
    -   System checks if adding another run would exceed the worker allocation (30% or 50% of total workers)
        
    -   Only actions within this concurrent limit will be allowed to start
        
    -   The remaining actions wait in a ‘pending’ state until there is capacity (either concurrent action usage falls below the limit, or sufficient tokens are available) wait for capacity
        
-   2\. Then, for the running actions:
    
    -   Each action running for 10 seconds consumes 10,000 tokens (1,000 tokens/second × 10 seconds)
        
    -   Total consumption is 50,000 tokens (5 actions × 10,000 tokens)
        
    -   The story still has 1.45 million tokens available
        
-   3\. When any of the running actions complete:
    
    -   Concurrent capacity becomes available
        
    -   The system checks token availability (1.45 million remaining)
        
    -   If tokens are available, the next waiting action can start
        

### Autoscaling to keep up with the demand

We ensure that our workers can auto-scale to match story run demand as needed in the Cloud. This is based on the percentage of workers currently available to process action runs on a stack. If the system notices that the percentage of workers available is less than the defined set of thresholds, workers are automatically scaled up. We do this preemptively to ensure action runs are not queued for too long.

Implementing fair orchestration in our action run logic ensures all stories receive an equitable share of compute time, and to ensure action runs are processed consistently with balanced worker allocation across the stack. Our goal here is to guarantee your action runs are getting enqueued and started as swiftly as possible, always.

## High priority stories

Tines allows you to designate specific stories as high priority. This is useful when you have critical workflows, such as incident response or time-sensitive alerting, that must process action runs as quickly as possible, even during periods of high load across your stack.

### What does high priority do?

When a story is marked as high priority, its token bucket capacity is doubled from the standard limit:

| Story type | Token bucket capacity | Worker time equivalent |
| --- | --- | --- |
| Standard story | 1.5 million tokens | 25 minutes |
| High Priority story | 3 million tokens | 50 minutes |

This means a high priority story can sustain significantly more concurrent action run processing before its token bucket is depleted, reducing the likelihood of action runs entering a pending state during heavy usage.

> Note: High priority only affects the token bucket limit. Concurrent run limits (50% of total workers for single-tenant stacks) still apply equally to all stories.

### How to mark a story as high priority

To designate a story as high priority:

1.  Open your story in the storyboard.
    
2.  In the properties panel, locate the High Priority setting.
    
3.  Select **High priority**.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2329217106/7d411393e6fbb7ca2e7d3afd2318/high+priority.gif?expires=1784786400&signature=549d6827ae2a872bb3b1d2c392ce4aeb5b32353cd127bcf21b66dc25898fcb84&req=diMlH8t%2FmoBfX%2FMW3nq%2BgZBNRYB64XqvptDnPOJ0d9j1UpFvMZp3n3vMLtN0%0Axg9Br8M615PicF90aRGxcI4ct60%3D%0A)

> Note: The High Priority setting is only available on single-tenant Tines deployments. It is not available on multi-tenant (cloud) stacks.

### When should you use high priority?

Consider marking a story as high priority when:

-   The story handles critical security incident response where delays are unacceptable.
    
-   The story is a time-sensitive alerting pipeline that must process events ahead of other workflows.
    
-   You have identified that a specific story is frequently entering a pending state due to token exhaustion during peak load
    

> Tip: Use high priority sparingly. Since all stories on a stack share the same pool of workers, designating too many stories as high priority may reduce the benefit for each one. Reserve this setting for your most critical workflows.

## Story toolbar

The story toolbar in Tines includes our actions, tools, and templates. It is a fully customizable floating toolbar that appears as a floating panel on the storyboard, designed to help you build faster. Actions are grouped by function so you can find what you need at a glance.

> Read about the storyboard in Tines in [this article](https://explained.tines.com/en/articles/12709994-introduction-to-the-storyboard).

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332295854/51eb81bf7c5cce2511dde12a6b14/Storyboard+UI.png?expires=1784786400&signature=0d329f7e77e799ba2a22fcaa4609d27b6c282f0472a1cb821f68577b8105a3f3&req=diMkFMt3mIlaXfMW3nq%2BgRjpAGFOQY0P%2Fcr2XIlNGxwmiDe3nWQKtRS6hjzW%0AyRXSiA09%2B9tIImGcXpntHuhByqU%3D%0A)

## Accessing the toolbar

The story toolbar appears as a floating panel on your storyboard.

Click on the toolbar to expand it and access the full set of actions, tools, and templates.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325596862/18c9967a78a455dccd91739a33fc/toolbar+location2.gif?expires=1784786400&signature=4b555b775b455964ba34befbbf43089d8a571d681ecd6465522bc9e1bed920cf&req=diMlE8x3m4lZW%2FMW3nq%2BgXOIISpu2ujmtM3Pjn0dS8Pnuf1RrGfCPJiCgJyQ%0AN2a4adv16vt7HxcdL%2FzJcNR5NKE%3D%0A)

> **Note**: By default, you'll see the most commonly used actions

## Opening action modes

Some actions in Tines have different modes, such as the event transform and AI agent actions.

To access these action's modes in the toolbar, click on the actions that have an arrow in the corner of the icon.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325286138/9e403cc920d342e534385061a35b/9f9d3037-6cb7-4917-8438-5feca6e6b734?expires=1784786400&signature=8fcb0b88f226f5359ba6f25817018f94b5d5408c797ae0261c77fb93a1657cf4&req=diMlE8t2m4BcUfMW3nq%2BgQM1apSke7dYSQWko3jfLT01nr1LUyqzqmAvWdbG%0A56YCK5ybOH7yRqi4G0%2FVPzhW1bM%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325659843/b3e0db552a4c0e355117ab954792/toolbar+modes2.gif?expires=1784786400&signature=00470a6be92b4d30c533aaca9df80e7e1e7d6a02d9d317161d5f687578d4db32&req=diMlE897lIlbWvMW3nq%2BgS7RJEfRfJiWWcp6aOhG38jqRBQSCAESjCkR8336%0AaZuzG2JW%2Bv9%2Bnpobshws4%2BGhEiI%3D%0A)

> Read about actions in Tines [here](https://explained.tines.com/en/articles/6872936-what-are-actions-in-tines).

## Template library

Templates in Tines are pre-built actions that are ready to deploy in seconds rather than building from scratch.

> Read about templates in Tines [here](https://explained.tines.com/en/articles/12709787-templates-in-tines).

The template library can be found within the HTTP request action.

Click on the HTTP action to open up the library. Here, you can use the search functionality or navigate through the templates.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325674120/ba578e888544b0c114d1ebbfc5f4/toolbar+templates2.gif?expires=1784786400&signature=ba1dc985dc6e655ca234954f0465fe9bece32ca7ec55e843654911654caf1878&req=diMlE895mYBdWfMW3nq%2BgRT7mjTwnqpk4xjxU17CImQDxrRe%2F7AVHW2qTsB2%0A2Ik6jdkygADRRDDG4ZPTWn3XEi0%3D%0A)

> **Tip**: Press T on your keyboard to go straight to the templates menu

## Customizing the toolbar

There are a number of ways you can modify the story toolbar to suit your own preference.

A custom toolbar is saved for each user, per tenant — so every team member gets a toolbar tailored to how they work.

### Add an item to the toolbar

Simply search for a template or action, then drag and drop it onto the toolbar.

You can search by either using the search functionality in the toolbar or by navigating through the actions.

> **Tip**: Press A on your keyboard to open the search in your toolbar

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325625884/e8b0761e6c73dc8b67b9eb04aca0/toolbar+add2.gif?expires=1784786400&signature=79d4fdd9b9cc8f208b40d5f7a928c9d4e5f88bc84df06ac927cceb0230dee2b1&req=diMlE898mIlXXfMW3nq%2BgRunpqlC0y8ZZOwTBeqhYhg27tLm8%2FAy4jl2fpty%0AOr1cAhB4OiJK1ooMmPYZo0D473s%3D%0A)

It is possible to add specific action modes to the toolbar.

For example, you can add the explode mode for the event transform action directly on your toolbar.

> **Note**: There is a 14 tool limit on the toolbar.

### Remove an item from the toolbar

To remove an item from the toolbar, open up the popup menu and drag the item you wish to remove.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325608837/09efa91836244de4dd16753eacdf/toolbar+remove2.gif?expires=1784786400&signature=184765e5974f9028cc13b89e380436aaed7c74108996e41428e1b65b2b439fa9&req=diMlE89%2BlYlcXvMW3nq%2BgRiuZg9DpT%2FlDhqUHmAvkRS2N9fh8WjqVftIk%2B71%0ABnNEVEBG2e7KFzIubD%2F9MV%2F%2Bss8%3D%0A)

Alternatively, you can right click on an item and click on **Remove from toolbar**.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2359869946/96734ec6bce36d382dbf31e43297/CleanShot+2026-05-07+at+09_25_35%402x.png?expires=1784786400&signature=01fe126f9645f86fcb0ef7eba82b7a60245c01ab632f7219ab5f5853d026c2e1&req=diMiH8F4lIhbX%2FMW3nq%2BgTmiVka8%2BfoJIXIHUyxEHPbJW%2FlhRtBB%2FcG3AWVW%0AoLEt8JayVfdvO41Yq%2FKcVtXKDss%3D%0A)

### Rearranging the toolbar

You can easily rearrange the order of your items in the toolbar. Just drag and drop the items you wish to move around.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2325589295/f342ea65743724fb29a43099e816/Toolbar+rearrange2.gif?expires=1784786400&signature=d93d706d19b42f686488937bef096050c621d2917ee89503dae1f51179347d01&req=diMlE8x2lINWXPMW3nq%2Bgd8VAtrgc8gAr8rjTmzsn5YQgTl885dchlWR8z1N%0ADIdl4Q6lVmiaFJuaDZ6fVn9sd0U%3D%0A)

## Sublime Security Authentication Guide

Sublime is the world’s first open email security platform that lets anyone write, run, and share rules in a universal domain-specific language (DSL) to block email-borne attacks, hunt for threats, and more.

## First, get a Sublime Security API key

1.  Login to your Sublime Security account at [https://platform.sublimesecurity.com/login](https://platform.sublimesecurity.com/login)
    
2.  Click "API" and then click "New Key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/860298960/bea303388aa428477d0ba713/image.png?expires=1784851200&signature=b64c253a652d2a4de65bd8503ef69893485c1388696b696ca11e31c62bad6570&req=fCYnFMB2lIdfFb4V1XW4gQgXFrN1VjbsGYwi5YIDHf0KaymC4AG8O68ptW2B%0AClpousyJ2Dsc7gMJa2gvhmqNRA%3D%3D%0A)
    
      
    ​
    
3.  Name the key and click "Create"
    
4.  Copy your "API key" to a text editor
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/860299289/41ac8b5cdbe041e6879454e0/image.png?expires=1784851200&signature=c7897be57ffffa436e196c1af29c0c3334949f2a0008d570aa041450a14840ed&req=fCYnFMB3n4lWFb4V1XW4gT3ZOGCHK8Mc4CzNk4kBezr8vR0acjRr7qhubW2a%0AaC5dKE%2FE%2B%2FNZMOk0y3kmeIxjKw%3D%3D%0A)
    
      
    ​
    

## Then, create a Sublime Security credential in Tines

**Sublime Security connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Sublime Security" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the Sublime Security connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Click "+ New Credential" and select "Text"
    
2.  Input the values for the Sublime Security credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** Your copied API key value
        
    
    1.  **Domains:** Your Sublime Security API domain. For Sublime Cloud customers, it is \`https://api.platform.sublimesecurity.com\`
        
    2.  **Access:** What other teams can also use the API
        

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/sublime-security).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of Sublime Security stories in the [story library](https://www.tines.com/library?s=sublime%20security&view=all)

## Sumo Logic Authentication Guide

Sumo Logic enables customers to deliver reliable and secure cloud-native applications. They help practitioners and developers ensure application reliability, secure and protect against modern security threats, and gain insights into their cloud infrastructures. Customers around the world rely on Sumo Logic to get powerful real-time analytics and insights across observability and security solutions for their cloud-native applications.

## First, get a Sumo Logic Access Key and Access ID

1.  Login to your Sumo Logic account
    
2.  Click on the user menu and click "Preferences"
    
3.  In "My Access Keys", click "+ Add Access Key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929250434/7fe83cd58250e157489bffa9/image.png?expires=1784851200&signature=5979173ddb75694e96e3f2281c06b7195e1857e5af01d20d5ac5b38223a2c05e&req=fSIuFMx%2BmYJbFb4V1XW4gSg5HBdtX%2BBgxO6bgZZoBgjuicLga1iSwtvZwq5g%0AuU2nh8Oc8ryTTegulaUWHSUerQ%3D%3D%0A)
    
      
    ​
    
4.  Name the Access Key and click "Create Key"
    
    1.  Optionally, you can add domains to the allowlist one at a time by clicking "Add"
        
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929250997/571812ba9dcee90a25194c50/image.png?expires=1784851200&signature=52ccb3c1bee9e4bcc9e57bb464563350b27168525d50ac90ef7a4afd91daf0f5&req=fSIuFMx%2BlIhYFb4V1XW4gc7DEcIEdj3IKS1IUk4YlWVXHpRR8mjsciMO8gL1%0AJVz383l5r4cI06VtNAW4R4PR0Q%3D%3D%0A)
    
      
    ​
    
5.  Copy both your Access ID and Access Key to a text editor and click "Done"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929252269/a7c8407c772bb767dfb82ba5/image.png?expires=1784851200&signature=138951ecee42e3b2e70375a0a57f8bbbabcb3427d3aad11acbd0b8f5407db14f&req=fSIuFMx8n4dWFb4V1XW4gZLiQE8Ou1nclxgSNCfUJrSO8W1gY7%2B8ULW37UOQ%0AuaFDB6m1EY7mg7MAkcGFuypseg%3D%3D%0A)
    

## Lastly, create Sumo Logic credentials in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Sumo Logic" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/sumo-logic).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Sumo Logic** **stories in the [story library](https://www.tines.com/library?s=sumo%20logic&view=all)**

## Sysdig Authentication Guide

Sysdig is a universal system visibility tool with support for containers. What makes Sysdig special, is that it hooks itself into the machine’s kernel and segregates the information on a per-container basis.

## First, get a Sysdig API key

1.  Login to your Sysdig account
    
2.  Hover over your username and select "Sysdig API Tokens"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/905910928/69f7ad8c6527d3210c3a3612/image.png?expires=1784851200&signature=6ff6bff5f15a28ecf5e201573a327da0857458b597d186b6f851e536a2244d20&req=fSAiH8h%2BlINXFb4V1XW4gcGvmW7dRHI%2BuvJT3Zyjca%2F9kF6qLXQoydxuwmmd%0A9Tdenu7B0crhQwVICUGiLMMPpw%3D%3D%0A)
    
      
    ​
    
3.  Copy your API Token  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/905911241/6a37fdefbccade9127f9e643/image.png?expires=1784851200&signature=89d45008173e47d6085a6c29bc4f2415b67013b3f044842e3c625cd7396a93fc&req=fSAiH8h%2Fn4VeFb4V1XW4gW5YbipPZirY1GiESs%2Bbcb0eI65PqCch8YkVItj1%0AtHcemcy%2B2f9WFS%2FX4Cjr3P%2FWvg%3D%3D%0A)
    

## Lastly, create a Sysdig credential in Tines

**Sysdig connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Sysdig" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/sysdig).  
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Sysdig** **stories in the [story library](https://www.tines.com/library?s=sysdig&view=all)**

## Tanium Authentication Guide

Tanium is a cloud-based endpoint management solution, which assists large enterprises and government organizations with firewall management and threat monitoring. Key features include policy enforcement, status tracking, incident management and reporting.

## First, get a Tanium API key

1.  Login to your Tanium account
    
2.  Click on "Administration" and the click "API Tokens"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929238060/fda0ca69a635af5ffec3175e/image.png?expires=1784851200&signature=173ff58fe5525381a7fa120c6cecc043ac0e50618f39345845b78285ea50d9e4&req=fSIuFMp2nYdfFb4V1XW4gSqOY6SFoWR8SuxPgPTikeNnecQi5sagqmRqz4DM%0AWv7DsEr4kslU0L5n7e6Vro387w%3D%3D%0A)
    
      
    ​
    
3.  Click "New API Token"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929239997/b458690929c785443d2d4a9c/image.png?expires=1784851200&signature=ff4ffaaf9b6d10be9f2b87d1f86a8418993073f7b80a9b4d67525212b9d0f8c7&req=fSIuFMp3lIhYFb4V1XW4gYRkjZX53YRHzUoaORFTWhfq8bghaw6LvXOPrMqL%0AxQPfapk8H9g5OQO7gEsx%2BbDUDg%3D%3D%0A)
    
      
    ​
    
4.  Give the token a descriptive note, set an expiration, add a trusted IP address and click "Save"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929240102/ffb61257fc3caadda8f90819/image.png?expires=1784851200&signature=44e5831843839ab280af358d74aeb1f1c2f34ccaab57d2421858ec9499455a24&req=fSIuFM1%2BnIFdFb4V1XW4gc0D5aq8oJE0vBHv0AKRzwVnYf7ebfbTNKMZCTRT%0ANxsaI5wznMfLL8fHTIH4kluDUA%3D%3D%0A)
    
      
    ​
    
5.  Click "Yes"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929240224/62eec815efef71e687ec1d62/image.png?expires=1784851200&signature=bc4fa1bc7c4ab74461afb5858ecbb1b19278b1ada811456a52541cd36ec1dee8&req=fSIuFM1%2Bn4NbFb4V1XW4gXsaIOMGFqA%2F3dSuXr7rBA9DpysQvT6qIoJ7nTRG%0AgHaGRFJ1yqjqKW33oQCokfY2FQ%3D%3D%0A)
    
      
    ​
    
6.  Copy your API Token to a text editor  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929241529/96ef48583fef0f973055b1b4/image.png?expires=1784851200&signature=30e1c7919dd6c50df1e0dd85de5355718a262385042de2cb87fbe3945fb7d869&req=fSIuFM1%2FmINWFb4V1XW4gS%2BiU0RLcvPsAWlTgYVU3hVEuCfkoaFRa9b6UYyb%0AGDSehMBVyvl8NhMH%2B%2FYwFFk%2B7Q%3D%3D%0A)
    

## Lastly, create a Tanium credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Tanium credential
    
    1.  **Name:** Tanium
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API Token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Tanium** **stories in the [story library](https://www.tines.com/library?s=tanium&view=all)**

## Teammate credential access

## Creating credentials

Users with **admin** or **editor** roles within Tines can create credentials.

## Viewing credentials

Users with a **viewer** role have the ability to "look" at the credential by clicking it under "Credentials" in the team, so they could look at how a credential is configured. They can only see \*\*\*\*\*\*\*\* for any sensitive data.

## Deleting credentials

Tenant owners can delete any credentials across all teams

Team admins can delete credentials created within their assigned teams.

> **👉 Learn more about roles in Tines [here](https://www.tines.com/docs/admin/teams/).**

## Technical Limits

Included below is a list of some of the limits to be aware of when building in Tines:

| Item | Limit |
| --- | --- |
| Event | 100MB |
| Record File Attachment | 10MB |
| Page file upload | 20MB |
| HTTP Request | 30MB |
| Event Transform | 50MB |
| Webhook Payload | 30MB |
| Resource | 5MB |
| Downloads | 75MB |

## Templates in Tines

Templates are your secret weapon for building powerful automation quickly and consistently. Think of templates as pre-built action patterns that are ready to deploy in seconds rather than building from scratch.

## Template types

There are two types; public and private templates.

### **Public templates**

These ready-made configurations connect to popular services and products. Simply drag the product onto your storyboard, select your task, and the template handles the complex configuration details automatically.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1807150992/84908a0f923b8862b64a5cd232db/templates.gif?expires=1784851200&signature=ab01e7fdf89f0f51290f553df577e78a26ad909a098cb9c1aeb8be9734b75b08&req=dSgnEch7nYhWW%2FMW3nq%2BgXpuovctDcnwUOxiIPMEi4frFmPfJQ50ebcNPFNf%0AJvcOvQdKQLMfZ4qIbE%2BpWkfcCkc%3D%0A)

### **Private templates**

Create your own reusable patterns for team-specific needs. These custom templates become your automation building blocks, ensuring consistency while dramatically reducing build time.

To access your private templates, click on your user menu → **Templates**:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806474981/42f3a15d47c6f93486dd918d4cbc/Screenshot%2B2024-02-12%2Bat%2B11_39_53%2BAM.png?expires=1784851200&signature=6c2a3cb6509ebf2d4492e5efa9a751595276e36776af412ec398620972a7a640&req=dSgnEM15mYhXWPMW3nq%2BgRVHVX2jqrrB4kUgLEiFQ7JAiDAUkn62TT5UHkEW%0ALS3iLFUPgWL5najfZa5DCajo130%3D%0A)

#### **Edit private templates**

To edit a private template:

Within the **Templates** menu, click on the template you wish to edit to open the **Edit Template** window:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806474980/e78918e44254bdde5dbd37092eb3/Screenshot%2B2024-02-12%2Bat%2B11_44_35%2BAM.png?expires=1784851200&signature=90142d7cc4c0fad19770c0724b87b937b2d9beba86462ac16ddeefe6829dccb3&req=dSgnEM15mYhXWfMW3nq%2BgRXoTaQMGGdRDa03TWPBsQojkLdvvBHsZKnSQyA9%0Ay%2Be4Ae5%2Bq1Set6OwkgtTUryWwwI%3D%0A)

#### **Delete private templates**

To delete a private template:

Within the **Templates menu**, click on the three-dot menu next to the template you wish to delete → **Delete**:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1806474979/8e88eaa10363855ac352102a6c18/Screenshot%2B2024-02-12%2Bat%2B11_45_52%2BAM.png?expires=1784851200&signature=422deecc9f48a07953650eca88be73f892368d1d4fbe633316bb2ad6a2faa7a4&req=dSgnEM15mYhYUPMW3nq%2BgbjXKzLWUQiC4jjFycMQaAoQ98YoUYGZyIJSyVHu%0A7BF9feJoPMkWjTTwvx95NWERKt4%3D%0A)

#### Filter private templates

Filter your private templates by product by clicking the All Products drop down menu at the top of the templates menu screen.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2269941859/b6c6463c2ca5baafe22f574d9727/filter+templates.gif?expires=1784851200&signature=414db680c3ea6af0b98e77db63e3006e636f9e8ce8f03b8bf2f8af7ca035181d&req=diIhH8B6nIlaUPMW3nq%2BgQa51B7dtZmrK%2Bm2ovMSBOZ8xNFbog%2BoiOMH0MNQ%0A0mQXI8%2FeRXQJgsYn7cjPh2VdgH4%3D%0A)

> **👉 For more information on private templates, see [here](https://www.tines.com/docs/actions/templates/private-templates).**

### Templates best practices

To get the most from templates:

-   Start with templates, then customize: Begin with a template and adapt it to your specific needs rather than starting from zero
    
-   Create templates for repetitive patterns: If you find yourself building the same action structure repeatedly, convert it to a private template
    
-   Document your custom templates: Add clear descriptions so your team understands when and how to use each template
    
-   Keep templates focused: The best templates solve specific problems rather than trying to be all-purpose

## Tenant event limits: Best practice guide

Effectively managing your event limits in Tines is crucial for ensuring the smooth and continuous operation of your intelligent workflows. By proactively configuring limits and setting up timely alerts, you can prevent a single active team or runaway story from consuming your overall tenant capacity.

[This article](https://explained.tines.com/en/articles/12688669-what-s-an-event-in-tines) will give you a refresher on what an event is and what the event limits are for each plan in Tines.

Here are the best practices for managing your tenant event limits:

## Allocating limits to teams

A best practice for tenant owners is to strategically allocate event limits to individual teams.

### Prioritize active teams

Allocate a higher number of events to your most active teams. This prevents one busy team from exhausting the overall tenant limit and impacting other teams.

For example, a tenant owner can assign 15,000 events to the security team from the 20,000 total tenant-wide limit.

> Note: While stories within your Personal team do not count towards licensing (as they cannot be published), they do count toward your overall tenant-wide event limits.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2196116345/177a38dfaa19e3c637bc3ecf7fbb/tenant+team+limit.gif?expires=1784851200&signature=22a0fd16df12f94c8ba4b6061ef1ec410fe43e2f59c12a380abf153915b201aa&req=diEuEMh%2Fm4JbXPMW3nq%2BgYCsvvjSxW7lkSBuyImHhu364phmuNflYdrQwOvR%0AjEA4kQec4qq6ihhpedny3hqMW7g%3D%0A)

### Tenant health

You can utilize the tenant health page in Tines to identify which stories and teams to prioritize. The tenant health page includes a "Most active stories" graph that you can see in the below image along with a list of the most active stories (via action runs) and their associated team for a given timeframe (default: 7 days).

![](https://downloads.intercomcdn.com/i/o/am0g137l/2196116667/cbcb1f300f8f373cd58e34e860d2/d4795f2f-6eff-4e20-ba75-ec843e6e588f?expires=1784851200&signature=e97140b0b7b9c81d264599a048883d33cd06b85cf992d95050a0a640e2714a8a&req=diEuEMh%2Fm4dZXvMW3nq%2BgQvubeg28HZdWiwQVdwZot48iAGN%2B9JxBAl87hM0%0ACVU%2BfSxlKxC5Xv6Qsg%2BZUxkPAy4%3D%0A)

You can use this information when allocating team event limits, especially in longer-standing tenants where your teams' needs change.

## Configuring timely notifications

Timely alerts are essential for staying ahead of your event usage. Automated notifications are sent to story owners and tenant owners once usage reaches 80% and 100% of the daily limit by default. Tenant owners can manage event limit settings for tenant-wide, team, and global story thresholds.

### Set custom thresholds

Utilize custom notification thresholds to receive alerts before reaching critical limits. You can set notifications to trigger when an event limit from 40% is reached.

### Choose your notification method

Alerts for event thresholds can be delivered via two primary methods:

-   **Email**: Send notifications via email to configured recipients.
    
-   **Webhook action**: Route notifications through a [webhook action](https://www.tines.com/docs/actions/types/webhook/) to external platforms such as Slack, Microsoft Teams, or Jira. This method allows you to build a story that sends alerts from the webhook action to a specific channel or automatically opens a ticket when limits are approached.
    

### Notification recipients

When managing event limits, you can specify who receives notifications for threshold alerts, this includes both users and webhooks. You can also specify which team's limits they should be notified about.

For example, a team admin for the security team can be configured to receive alerts specifically for that team's limits, without having visibility into the tenant-wide limits.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2196118942/60d26707113d09bd92d1edb29dc8/4327f3c8-7fac-4115-98c8-08972dfa7180?expires=1784851200&signature=a454d8dd905a4b10d875f11a81763cdfae744b982d08c079dc2fd6bb62440bcb&req=diEuEMh%2FlYhbW%2FMW3nq%2BgQu6kK1MvSPXAmTyToHtarlen5Y22AuV4QCbJpMu%0AqLOaZralxYMsuxTtloZ2sMXnrNQ%3D%0A)

## Increasing your event limits

If your tenant is consistently hitting its maximum daily event limit, you will need to discuss an increase with your account owner.

> Note: Tenant wide and global story event limits are configurable on self-hosted tenants.

Read more on event limit settings in our docs [here](https://www.tines.com/docs/admin/event-limit-settings/).

## Tenant owners

> **Note:** A Tenant owner is required to create another tenant owner. If the existing tenant owner is unavailable, [contact](https://www.tines.com/getting-answers-to-your-tines-questions) us—we’re here to help.

Tines can promote ***existing*** users to tenant owners for your tenant. Contact [Tines Support](https://www.tines.com/contact) and provide the following information:

-   The full tenant URL. (i.e. `https://your-tenant-domain.tines.com`)
    
-   The user's email tied to the account within the tenant.
    
-   Someone within the tenant (it cannot be the requester) will need to provide us with a secondary approval for this change. They can send an email to [support@tines.com](mailto:support@tines.com) that states “I approve (user's email here) to be added as a tenant owner."
    

Once we have obtained this information, our team can begin processing your request. Requests can typically take up to 48 business hours to complete.

> **Tines Tip:** For more information around tenant owners and other roles, read more here: [User administration](https://www.tines.com/docs/admin/user-administration)

You can also create tenant owners within your tenant. Select the menu at the top left.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810213921/f2dc528f4a55ec1e5e68d87918e7/Screenshot%2B2024-08-27%2Bat%2B09_42_04.png?expires=1784851200&signature=528182efe720e576bbcbac4d7ae08a9dc0d2e4425ef33673169850c598d418a5&req=dSgmFst%2FnohdWPMW3nq%2BgRvs5wYBRlaZUrzdsmXTPHhN2ljbjAWc3xGz6OvA%0Azan8fQs1cTqC2UkFWxFOUhOIvCA%3D%0A)

Select "Users" and double-click on the user you wish to make an owner. Toggle the "Tenant Owner" button as needed.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810215242/3db5da3a3a81d7f014b1b5c98949/CleanShot+2025-11-03+at+10_33_35%402x.png?expires=1784851200&signature=180ddbb3c46c6ac7965774ffd7e60540e9513cc703171591eef4b3a1ff9e05cd&req=dSgmFst%2FmINbW%2FMW3nq%2BgRqv7tjJnD%2FOpx4iS%2FZ0sWak8nEzgmia0XErXIal%0ADnaQI3DTJGCfmyufikxNl9xq5OY%3D%0A)

> **Note:** To receive emails regarding your tenant and tenant limits, you must be a tenant owner.

## Tenants and teams

Understanding the structure of tenants and teams in Tines is essential for building a scalable, secure automation program. This organizational framework determines who can access what and how your automation assets are managed.

Tines' tenant and team structure provides:

-   Clear boundaries between different automation domains
    
-   Appropriate access controls for sensitive workflows and credentials
    
-   The ability to share resources selectively across teams
    
-   A foundation for scaling your automation program as it grows
    

### **Understanding the Hierarchy**

**Tenants** are your organization's Tines instance that are created when you purchase a Tines plan or sign up for the community edition.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1807124667/5923d8e8c0a467880723413765e6/CleanShot+2025-10-31+at+17_22_56%402x+copy.png?expires=1784851200&signature=c0e602636fccacb9222e15dc07aa7045836f6308872af8bcb5b2270b152117e6&req=dSgnEch8mYdZXvMW3nq%2BgWqOg%2Ft51VMHz0%2FjU%2Fg470IPdP5Gqs15P782%2BBl7%0AzpOLfOm8sfOos7ptY%2FAVd2L4Eeo%3D%0A)

**Teams** are workspaces within your tenant where specific groups collaborate.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1807125144/49197cbd27d36f6f0c1239831511/CleanShot+2025-10-31+at+17_22_56%402x.png?expires=1784851200&signature=1a50d095bb9fe8fb83ee1f1149004ed9467b16326dc4e0d49d749cf056d543f5&req=dSgnEch8mIBbXfMW3nq%2BgUegOTGXEmC8snVWcsAV1K%2FL6bf6TGin9uAbMj0G%0AkiVBEUnj5703LEw25xev6rMu3SM%3D%0A)

Each team has its own:

-   Members with defined roles (Admin, Editor, Viewer)
    
-   Stories (workflows)
    
-   Resources and credentials
    
-   Access controls
    

### **Best Practices for Tenant and Team Organization**

To build an effective automation ecosystem:

1.  Structure teams around functional areas: Create teams based on departments or functions rather than projects.
    
2.  Use roles strategically: Assign team roles based on what members need to do - not everyone needs admin access.
    
3.  Share resources thoughtfully: Make common resources available across teams while keeping sensitive credentials restricted.
    
4.  Plan for growth: Design your team structure to accommodate your organization's future needs.
    

By thoughtfully designing your tenant and team structure, you create an automation environment where collaboration is enabled while maintaining appropriate security boundaries.

## Tenant settings

Tines settings provide tenant owners and administrators with centralized control over their workflow environment. From managing user access and authentication to configuring security controls and AI providers, settings enable you to customize Tines to meet your organization's specific requirements.

### Where to find settings

To access settings in your Tines tenant:

1\. Click on your team name in the upper left corner

2\. Select "Settings" from the dropdown menu

![](https://downloads.intercomcdn.com/i/o/am0g137l/1811007303/c5dfcf5e19ad3538823f35fb533d/CleanShot+2025-11-03+at+17_23_55%402x.png?expires=1784851200&signature=6304c784cfd97dcda8343f63acc7bfe768fdb9f14d5f4989abd61c6c625d043f&req=dSgmF8l%2BmoJfWvMW3nq%2BgT24PGLoAsQIf8Nwj5oGiPmcF8mJJAP0qpAERAht%0A0UyPaLv0DewnwpDHjUcBPgm01Ic%3D%0A)

The settings interface is organized into these main categories:

-   Misc.
    
-   Configurations
    
-   Access & security
    
-   Monitoring
    

> You can hit **/** anywhere within your settings to start a search.

#### Misc.

These are commonly used settings:

-   **Users:** Manage who has access to your Tines tenant, assign roles, and control permissions. Add new team members, adjust their access levels, and organize users into appropriate teams for collaboration and security.
    
-   **Templates:** Create, edit, and organize private templates for your team.
    
-   **Credentials:** Search through all connected credentials and control which teams can access specific credentials. Credentials can be deleted from here too. New credentials cannot be created from this view.
    
-   **Page access:** Review pages created in Tines. View in story, preview and customize from this view. New pages cannot be created from this view.
    

#### Configurations

-   **Action settings:** Configure default behaviors and parameters for actions across your Tines environment. Manage code execution, page settings and MCP server settings.
    
-   **Al settings:** Manage how AI capabilities are integrated within your Tines environment. Control access to AI agents, configure model preferences, and set usage parameters to balance capability with security requirements.
    
-   **Change control:** Implement governance for workflow modifications with draft and approval processes. Enable version control for your stories to prevent unauthorized changes to production workflows and maintain a history of modifications.
    
-   **Feature flags:** Toggle experimental or optional Tines capabilities on or off for your environment. Control access to new features, beta functionality, or specialized tools based on your organization's needs and readiness.
    
-   **Event limit settings:** Manage the maximum number of events that can be processed within your Tines environment. Set event threshold alerts and add notification recipients.
    

#### Access & security

-   **Authentication:** Configure how users access your Tines environment. Set up single sign-on (SSO), enable session timeout duration, force session expiry and configure user provisioning.
    
-   **API keys:** Create, rotate, and revoke API keys that allow external systems to interact with your Tines instance.
    
-   **Login notice:** Customize the message displayed to users when they log into Tines. Create informational banners, compliance notifications, or important announcements that appear during the authentication process.
    
-   **Action egress:** Configure an IP or FQDN allowlist to restrict access to the destinations where HTTP Request Actions are allowed to make requests.
    
-   **Custom cert authority:** Upload and configure custom CA certificates to enable Tines to establish trusted connections with internal systems using self-signed or private certificates.
    
-   **Story allocation:** Manage story capacity by allocating story limits to the teams within your tenant.
    
-   **Sender emails:** Add custom email sender addresses used when Tines sends notifications.
    

#### Monitoring

-   **Audit logs:** Review a comprehensive history of activities within your Tines environment. Access detailed records of user actions, system changes, and automation executions for security monitoring, compliance reporting, and troubleshooting.

## Text Mode vs Formula Mode: Handling Complex Data in Tines

## Overview

When building workflows in Tines that pass complex data structures between actions, you might encounter errors when APIs expect specific data types but receive incorrect formats instead. This commonly occurs when working with structured data like arrays or JSON objects that need to maintain their format when passed between actions. You, as a Tines user building workflows with structured data, need to understand the difference between text mode and formula mode when referencing data. This behavior occurs specifically when referencing output from one action in another, particularly when working with data types that need to preserve their structure rather than being treated as strings.

## Cause and solution

### Cause One

The default "Text" mode in Tines treats all referenced values as strings, even if they're actually arrays or objects. When you reference the output of an action that returns a complex data structure using the default text mode, Tines adds quotes around the entire structure, converting it to a string. The receiving action or API then tries to process this as the original data type but finds a string instead, causing errors.

> **🟢 Solution:** Change the data type from "Text" to "Formula" in the action configuration panel. In the properties panel on the right side of your action, locate the field where you're referencing the data, click on the data type indicator (which shows "Text" by default), and select "Formula" instead. This preserves the original data structure rather than converting it to a string, ensuring that arrays remain arrays and objects remain objects when passed between actions.
>
> ![](https://downloads.intercomcdn.com/i/o/am0g137l/1801567440/0d4a1fd5cfbb9463f7fefedc6721/image.png?expires=1784851200&signature=4919f098a8a36f494627956c6f3aae73cca904da95b4e9c2a6b2f6ce4eb6e008&req=dSgnF8x4moVbWfMW3nq%2BgTtHdyetWkdp5EZkXYF0oOuBgOGVjkTWEqvRULcR%0AME0i4aW32dA9%2FqApWribHnZQ4gE%3D%0A)

### Cause Two

The data structure you're trying to pass might not match what the receiving action or API expects. Even with formula mode enabled, if the underlying data structure doesn't match the expected format, you'll still encounter errors.

> **🟢 Solution:** Verify that the data you're passing matches the structure expected by the receiving action or API. You can check this by adding a debug action or examining the event logs to see the actual structure of your data. If needed, use Tines formulas or script actions to transform the data into the correct format before passing it to the next action.

### Troubleshooting checklist

✅ Check the data type setting for fields that contain complex data - switch from "Text" to "Formula" mode.

✅ Verify the structure of your data matches what the receiving action or API expects by examining it in the event logs

✅ Use the TYPE() function in a formula to check what type of data you're working with (e.g., TYPE(action\_name.output))

✅ For script actions, ensure you're returning data in the correct format (arrays as arrays, objects as objects)

✅ Test with simple examples first to understand how different data types are handled

✅ Remember that formula mode preserves data structure, while text mode converts everything to strings

## Need further assistance?

When contacting Tines Support about data type issues, please provide:

> **ℹ️ Additional context:**
>
> 1\. The exact error message you're receiving (if any)
>
> 2\. A screenshot of your action configuration showing the field where you're referencing the data
>
> 3\. The output of the action providing the data (with any sensitive information redacted)
>
> 4\. The expected structure of the data according to the receiving action or API
>
> 5\. Any troubleshooting steps you've already tried, especially whether switching to formula mode resolved the issue

## TheHive Project Authentication Guide

TheHive is a scalable Security Incident Response Platform, tightly integrated with MISP (Malware Information Sharing Platform), designed to make life easier for SOCs, CSIRTs, CERTs, and any information security practitioner dealing with security incidents that need to be investigated and acted upon swiftly. Here's our guide for authenticating TheHive for use with Tines:

## First, get a TheHive API key

1.  Log into TheHive.
    
    ![](https://downloads.intercomcdn.com/i/o/753626641/53dbdf02c00dbd16711a51ff/Untitled+%289%29.png?expires=1784851200&signature=296f6a3622af487b7c1f3732e55636e472133e3b252ff7d9026095354faf3d11&req=cyUkEMt4m4VeFb4V1XW4gYh5gxBaibeIf6lZdMNjI9bc1R3iAfviHjAGpMTz%0Aq4nvah7EO4nUbrGLTMXUJZ3YVg%3D%3D%0A)
    
2.  In the top right corner, click the profile icon and select "Settings"
    
    ![](https://downloads.intercomcdn.com/i/o/753626822/6933a81685d470d5183acca3/Untitled+%2810%29.png?expires=1784851200&signature=d54a6d6836b673f1515910fc5ca3f434ca3bb6e0e4ae39fac462d05bef4dcde4&req=cyUkEMt4lYNdFb4V1XW4gfODsFMq9%2F4J1970%2B3%2FgyhGPNAKU%2BcXlfzPfg%2FyC%0Aoo%2F2YIuH4149fFrXCBAHqIm1%2Bg%3D%3D%0A)
    
3.  Choose "Create" under the "API Key" tab.
    
    ![](https://downloads.intercomcdn.com/i/o/753626937/594c901bfeca5cb3a4e7a4ec/Untitled+%2811%29.png?expires=1784851200&signature=c4d2eab720e3b0319e84a043330f4450cbc411edc1c997fd8634ed37d9a302dc&req=cyUkEMt4lIJYFb4V1XW4gWK1lWvT43f0EeXr7QXp8vmgFwzyb7YUbDgTWEeg%0ALcWFJVRyNS2vnNYDcu%2Fm7jaj6g%3D%3D%0A)
    
4.  Copy your API key
    
    ![](https://downloads.intercomcdn.com/i/o/753627058/0dc3a2b1585cc5dd4dcc2d10/Untitled+%2812%29.png?expires=1784851200&signature=f20eb52b233ea4f750c43063c6a0aaa1bb9b19868e4057179b79427b23657f30&req=cyUkEMt5nYRXFb4V1XW4gU2zqsmdyNL7d7k%2FG94IzPwByk2sZIlochuJr5EX%0ABJhYiyJ%2BuHhjd94s5Ieho2Yv3A%3D%3D%0A)
    

### Lastly, create a TheHive credential in Tines

**TheHive Project connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "TheHive Project" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the TheHive Project connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "**\+** New Credential" and select "Text"
    
4.  Input the values for the TheHive credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
    4.  **Metadata:** Select 'Enable metadata' and then paste in the following in Plain code mode. Replace the sample URL below with your TheHive Project URL.
        
        ```
        {
          "url": "https://thcp1.aws.thehive-cloud.io/0abc1234-5678-9d01-234e-567890f123g45/thehive"
        }
        ```
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Learn more about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/thehive-project).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of TheHive stories in the Tines [story library.](https://www.tines.com/story-library?s=hive&page=1)

## The Tines Dictionary

## **A**

### API

You can connect to any tool that has an API with Tines. API is an abbreviation for Application Programming Interface. APIs serve as intermediaries that facilitate communication between two applications. In Tines, we typically use API as shorthand for *web* APIs involving HTTP requests between web servers. You can even [build your own APIs with Tines](https://www.tines.com/blog/build-apis-with-tines).

### Action

[Actions](https://explained.tines.com/en/articles/6872936-what-are-actions-in-tines) are the building blocks of all stories in Tines. They emit events to other actions in your story. Once you drag an action onto the Storyboard in Tines, you have started building your first story in Tines.

### Action bar

The action bar is the small menu of options that appears when you select an action on your Storyboard. You can do things like run your event, test your event, and see event data for that action.  
​

![](https://downloads.intercomcdn.com/i/o/am0g137l/1937026741/eca15a499521053aabf77ebd0b30/HvgTSz49IAGvurG5eL19j1yiGG3Mw_WLzEIXq1SXcMm0vmxVNSM2_2kwRuIKefPh1n-PYynKShjY7mBvADYxBxvbocutMiryS4l7l28KxWdvg5dsJr-P7XqdGrtmB68C_OKAxOBYcPG8UxmEwaTsMnI?expires=1784786400&signature=b83fd0728e0d5a0522e786d29e4cd42b8da0a1a3becc8e443f2e711cbb4af5bd&req=dSkkEcl8m4ZbWPMW3nq%2BgSCv3rz6wiS8abRvpgDRm%2BspJl2cfeJJrd8QykQE%0Am%2BlbqBk2H5LBpXY%2F48bRFDVgAn8%3D%0A)

### AI agent action type

A type of action that's available on Tines plans with AI features which 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. It allows you to configure system instructions (how the agent should behave) and a prompt (the task to be performed). You can also add tools to be used by this action. Read more [here](https://explained.tines.com/en/articles/11571096-introduction-to-ai-agents).

### **AI Lexicon**

|  | **Definition** | **Examples** *These are not exhaustive and are subject to change.* |
| --- | --- | --- |
| LLM | Large language models are the underlying transformative AI technology that both generate text and support the use of textual instructions to be turned into operations. | 
-   OpenAI: GPT-5.2
-   Google: Gemini 3 Pro / Flash
-   Anthropic: Claude 4.5 Opus / Sonnet
-   Meta: Llama 4 Maverick
-   Mistral: Mistral Large 3,Pixtral 2

 |
| MCP | Model Context Protocol is a web standard for a service to define what tools an AI system has access to. MCP is a way for an AI powered application to communicate with other applications, through a “client-server” relationship. (Essentially APIs for LLMs.) |  |
| MCP Client | Clients are LLM powered applications (agents, software applications etc) that want access to external tools. | 

-   Tines: AI action
-   Other: ChatGPT, Claude Desktop, MS Copilot, Cursor, etc.

 |
| MCP Server | Servers are the collections of tools that an application can make available to MCP clients. This enables MCP clients to access resources and take action. | 

-   Tines: build MCP servers in Tines
-   Other: Glean MCP server, Notion MCP server

 |
| Tools | Tools are functions an LLM invokes to achieve a task which may involve other systems. | 

-   Tines: A create Case tool enables an LLM to create a Tines case with a custom title, description, and more.
-   Other: Google Workspace MCP server allows you to create a new Google Doc or gDrive.

 |
| Agent | Agents are models acting autonomously using tools in a loop e.g. thinking, then using a tool, then thinking etc. until its purpose is achieved. | 

-   Tines: Task Agents
-   Other: Glean AI agents

 |
| Copilot | Copilots are LLM powered applications that work with a user in accomplishing a specific task. | 

-   Tines: Workbench, Chat Agents
-   Other: Microsoft Copilot, Salesforce Einstein Copilot.

 |

### AI monthly credits

All Tines plans receive a monthly allocation of credits:  
• Community Edition: 50 credits  
• Paid Editions: Starting at 2500 credits

Monthly credits reset on the first of each month and don't roll over. Each credit is equal to $0.01 USD. You can monitor the dollars in the reporting dashboard.

If required, you can purchase additional credits in your tenant settings.

For community users, the 50 monthly credits can be used for AI actions and Workbench.

### AI agent system instructions

System-level instructions that define the rules, context, and constraints for the AI agent action. In chat mode, this should define the agent's goal, where the chat ends when the goal is achieved.

### Apps

Apps are a customizable, interactive front end for your Tines workflows. Think of the app’s backend as the Story run, while the user-facing page as the front end.

### Apps endpoint

The entry points which connect a Tines App to Tines data and workflows. They can send data to or receive data from a story.

### Archived story

A Story that has been placed into a temporary pre-trash location so that they can be recovered. These stories are not deleted until you choose to delete them.

### Audit logs

Any time a user changes any piece of data in your Tines tenant, through the UI or API, it is automatically captured as an audit log. You can view your audit logs via your tenant admin menu in the top left of your dashboard when you click the Tines logo.

## **B**

### Build tab

A tab in the properties panel used to configure your actions.

![](https://tines-f60bb36f92a5.intercom-attachments-7.com/i/o/789436003/21f8554e796f9f15e60d7a11/UjZVae5zJ3nCVqHxbDKM4an-zOJEnwCb5kRfVJjXJapsc7l0nPUblcRRLStp3vbFm-4R4wuQrvbHD8tWBg7MmrkQe8xMIkpwV2mkeBFwKKGTgO988_dcimcD1pPkRKCrK63c_C1stpq4AX_EGp27hMI?expires=1784786400&signature=359f857d69af47598df0db7e5f5e1960641caf6d3fc7f4700d174264571f3410&req=cyguEsp4nYFcFb4V1XW4gYa%2F1Mb%2BqfrL0dLo6uj74eHz%2B6Cz11FtUyQCQWm%2F%0AVQUxMwRWRz6cVfyEogmhRbOlNw%3D%3D%0A)

## **C**

### Case

With [Cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases) in Tines, you can work together, comment, and track activity for any alert or incident.

### Case blocks

Case blocks are discrete, reorder-able sections. Each block has its own title, color, type, and visibility settings.

### Case fields

Case fields let you capture custom, structured data within your cases – like ticket IDs, affected systems, or investigation outcomes – beyond built-in case properties like name, status, and priority.

### Case input

A form-based input mechanism that allows users to manually provide data into a Case, typically surfaced through a Tines Page or form associated with the case.

### Case input validation option

A rule or constraint applied to a Case Input field to ensure the submitted data meets certain criteria (e.g., required, specific format, value range) before it is accepted.

### Case template

Pre-built structures for cases that include pre-defined fields, notes, and metadata. These types of templates allow you to quickly create new cases without starting from scratch, which is especially useful for workflows your team handles repeatedly.

### Change control

With [change control](https://www.tines.com/docs/stories/change-control) in Tines, you have two working spaces for each Story; test and live. These environments run independently of each other without any interference. You can enable Change control in the manage dropdown menu above your Storyboard. When your story is ready, admins can push story changes from test to live.

### Credentials

Authentication keys for the various systems and services you connect with Tines. Your [credentials](https://www.tines.com/docs/credentials) are saved in Tines, and accessible via the team they are saved within.

### Command menu

You can access a list of quick actions by using command+K in Tines.

### Command over HTTP

[CoHTTP](https://www.tines.com/docs/admin/command-over-http/) is an add-on feature that 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.

### Condition action type

One of the eight action types in Tines. With a condition action type you can define the rules or conditions that determine what happens in your story. The condition action compares the contents of a field from an incoming event with predefined rules. When the rules match, an event is triggered.

### Condition rules

Rules determine when and how condition actions should fire.

Connect flow

The mechanism in the Tines tool which guides users through the process of creating a credential for a specific product or tool. Each [connect flow](https://www.tines.com/docs/credentials/connect-flows/) is unique to its product and via secure browser window-to-window communication, will create a credential for the user in the team where the flow was completed. It supports creating various types of credentials including managed OAuth-type credentials (not available for self-hosted tenants).

## **D**

### Deduplicate mode

A mode of the event transformation action. Deduplicate mode is available in any [event transform action](#h_b344acf839). You can specify a field in an incoming event and only emit the event if that field contains a unique value.

### Delay mode

A mode of the event transformation action. With delay mode, you can deliberately pause a story for a set number of seconds before emitting an event. More in our docs [here](https://www.tines.com/docs/actions/types/event-transformation/delay).

### Dedicated cloud tenant

A **Tines dedicated tenant** refers to a separate, isolated instance of the Tines automation platform that is set up exclusively for a single customer or organization. In other words, it’s a private version of Tines where all the data, workflows, and automations belong to that specific customer, ensuring security, customization, and resource allocation tailored to their needs.

### Deleted story

A story that has been permanently deleted after having been archived. These types of stories, cannot be recovered.

### Description

The descriptive text you can provide about your story, action, page, record, or other story components.

### Drafts

A private place to build stories on your own before moving them to a team when you’re ready.

## **E**

### Editor

An editor in your team is a user that has read and write permissions to *most* objects across a team, but they cannot manage member access to the team or perform destructive actions. This role is helpful for most users who need the ability to create, edit, and run Stories in Tines.

### Element

Actions, tools, or templates you can pull onto the Storyboard to begin a workflow.

### Emits

The process by which an action generates and sends an event to subsequent connected actions within a Story.

### Event transformation action type (ETA)

One of the eight action types in Tines. Use the [event transformation action type](https://www.tines.com/docs/actions/types/event-transformation) to modify and transform incoming events or data for further downstream processing. It has several modes: message-only mode, extract mode, explode mode, deduplicate mode, delay mode, implode mode, and throttle mode.

### Events

Structured data sent between actions within your stories. Every time an action runs, it produces an event that describes the outcome or result of the action. This event is passed to other actions connected downstream. More about events [here](https://explained.tines.com/en/articles/6877834-what-are-events-in-tines).

### Event viewer

A panel that appears at the bottom of the Tines storyboard UI when viewing the event details for a selected action. This panel will also show the event details for upstream actions.

### Extract mode

A mode of the event transformation action. Extract mode allows you to use regular expressions to extract text from fields in incoming events.

## **F**

### Favorites

View a list of your favorite stories in your tenant. Favorites are available in the top left menu in your dashboard.

### Flow

A flow is a connected set of one or more elements, that can run automatically. In product terms, it is defined as:  
• 2 or more connected elements that can run autonomously  
• 1 or more scheduled elements

### Folder

Stay organized by sorting your stories into folders in your teams.

### Formulas and functions

Just like in spreadsheets, [formulas](https://www.tines.com/docs/formulas) allow you to transform data in Tines. You can reference event data from upstream actions or other data sources like resources and then use one of our 150+ built-in [functions](https://www.tines.com/docs/formulas/functions). For example, to count the number of items in an array, you could use a formula like \`SIZE(upstream\_action.array)\`.

## **G**

### Group

Organize and categorize related actions in a story together. Groups enable a better organization of complex stories by extracting well-defined branches of a story into a group.

## **H**

### HTTP request action type (HRA)

One of the eight action types in Tines. With this action type you can send/fetch data to/from any API on the web.

### HTTP request payload

The primary data that you send over in a http request. This is commonly known as "request body" outside of Tines.

## **I**

### Implode and explode mode

These are modes of the [event transformation action](#h_b344acf839). With explode mode, you can specify a field in an incoming Event that contains an array, and Tines will emit individual Events for each element of the array.

With implode mode, you can collect and reassemble a previously exploded array into a single event. Some examples include reassembling an array of 'exploded' URLs from a previous action in the story or consolidating API paging requests into a single event by setting the size to the number of pages available.

### Import

You can import a story into Tines in two ways: selecting a pre-made story from our Story Library or importing a story file from your computer via the import button at the top of your Tines Dashboard.

### Input action

An action entry point for either a send to story action or a Workbench request which contains defined values that either Workbench or the sub-story needs in order to run. These actions must be of type webhook.

### Integration

You can build integrations through Tines. An integration can be as simple as an action on the storyboard (see action templates) or it can be an entire story that's integrating multiple systems. As a vendor-agnostic platform, we can integrate with any API.

## **L**

### Local values

A place to do upfront formula calculations before an action runs. Then, when it runs, you can refer to the results of those calculations here.

### Lock story

There is the option to lock a story in the top right corner of the screen. Anyone viewing the story then needs to unlock it in order to configure actions or edit connections.

### Logs tab

A tab inside the properties panel to the right of your Storyboard. Here you can spot errors and analyze logs generated by your actions.

### Looping

An operation performed using the Event transform action to iterate through a list of objects & apply the same logic to each item in the list where the output would be a new list with the specified data. Read more [here](https://www.tines.com/docs/actions/types/event-transformation/message-only/#looping).

## **M**

### Message-only mode

A mode of the event transformation action. Use message-only to produce a new piece of data and emit it as an event for use downstream. You can use formulas to refer to upstream event data, and even use the loop configuration option to iterate over a list of data to transform it.

### Monitoring

You can turn on monitoring for your entire story. It will notify you when any action fails. You can also turn it on and configure it for individual actions.

## **N**

### Note

Add annotations or comments to provide additional information or instructions within your story. Available within the story toolbar

Notes support rich formatting including toggle headings (H1, H2, and H3), which allow you to collapse and expand sections to keep your notes organized. Type `/` in any note to access formatting options including toggle headings.

## O

### Output value

An action exit point or last action in a sub-story for which the output content is emitted by the originating send to story action or Workbench request. These actions must be of type event transform using the message-only mode.

## **P**

### Page

A web page you can make using Tines. Your end users can use it to fill out forms or consume information. You can build powerful apps with pages; they can display information from events, a workflow to end-users, and pass data from users to actions via forms.

### Page container

A grouping of multiple page elements on a page for which conditions and layout options can be applied. They can be created by dragging page elements on top of each other or by creating copies of a page element. Read more [here](https://www.tines.com/docs/pages/layout-and-containers/).

### Page element

When creating a Page in Tines, you can choose from a list of display elements that make up your page. These include title, divider, image, rich text, map and table.

### Page input field

When creating a page in Tines, you can choose from a list of input fields for your end-users to interact with. These include long text, short text, date, URL, email, file upload, and more.

### Page theme

You can organize and share visual preferences between your pages with themes.

Themes store the logo, color, and width settings for a page so they can be applied to other pages. You can create a new theme by opening an existing page and clicking on the theme dropdown.

### Pill

The Tines term for an entity which retrieves a dynamic variable data value. It's sometimes just referenced as "value pill" or a "data value pill". These are primarily used to call event data from upstream actions. In software development, these are similar to local variables.

### 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.

### Private templates

Team-scoped [templates](https://www.tines.com/docs/actions/templates/private-templates/) which are pre-configured actions created by someone on your team, and are visible to everyone on the team where they're created.

### Prompt

The specific details about the task or assignment that you want an AI agent to handle. For clarity, this can include an example of what you want the agent to output.

### Properties panel

There is a properties panel for your story, and another for each action item in your story. From the properties panel you can access everything from the story name, tags, credentials, to the story description. At an individual action level you can access the build pane, the status pane, the logs pane, and in the case of a HTTP request, the summary pane.

## **R**

### Receive email action type

One of the eight action types in Tines. The receive email Action emits events when it detects new emails on an IMAP server or when emails are sent to a uniquely generated email address hosted by Tines.Trigger workflows based on receiving emails or perform actions on incoming email messages.

### Recent stories

This story list is organized by your most recently edited stories in your tenant.

### Records

Capture and store data or information encountered or generated during the execution of your stories. You can drag a record onto your Storyboard to capture the data at specific points in your story.

### Re-emit

The operation of resendng the last event that was processed by an upstream action so that it can be (re)processed by downstream actions.

### Reporting

Tines reporting provides an overview of your Tines tenant and action performance. Reports include, story usage, time-saved per story, most activity by, story, action, credential, and resource.

### Resources

Manage and organize reusable pieces of data like text, objects, arrays and files for use across multiple stories.

### Run

The operation of executing an automation. Since the build and execution environments are the same in Tines, your most important workflows are ready to run and make an immediate impact.

## **S**

### Schedule

You can schedule your story to run at a specific time. Select the first action (or group) in your story, then go to status, and set your schedule there.

### Section

A visual container that can be used to associate a set of actions. They're meant to improve understanding and clarity by visually organising actions, making complex workflows easier to follow and interpret.

### Send email action type

One of the eight action types in Tines. Automate sending emails as part of your workflow processes.

### Send to story action type (STS or S2S)

One of the eight action types in Tines. Route events or data to other stories for further processing or analysis.

### Status tab

A tab in the properties panel where you can monitor and manage the status of your story or action or page.

### Story

A collection of actions connected together to achieve a specific automation goal or objective. It is the building format of a Tines workflow that may include multiple independent units.

### Storyboard

The canvas on which you plan for and build stories in Tines. This is where most of your work will occur.

### Story library

A repository or collection of reusable stories or workflow templates that can be shared and reused.

### Story runs

A story run allows you to trace the exact path an event took through a Story. It contains the Actions and their emitted events allowing you to easily troubleshoot and benchmark your automation stories.

### Story toolbar

The [story toolbar](https://explained.tines.com/en/articles/14781675-story-toolbar) in Tines includes our actions, tools, and templates. It is a fully customizable floating toolbar that appears as a floating panel on the storyboard, designed to help you build faster. Actions are grouped by function so you can find what you need at a glance.

### Sub-story

A Story that gets triggered by another Tines story via the send to story action. It may contain a set of actions that can be used in multiple stories to send data results back to a main story which triggered it.

It can be used to perform a common action or set of actions. All actions within this type of Story must be connected as separate since unconnected sets of actions, are not supported. The exit action(s) must be in the same connected flow as the entry action.

## **T**

### Tags

Tags are used in a few ways in Tines. You can assign tags to categorize and organize your stories in the story library, or your [cases](https://explained.tines.com/en/articles/7971194-introduction-to-cases); for easier searching and filtering. There is also a [tags feature used in formulas](https://www.tines.com/docs/formulas/language#tags).

### Team

With teams in Tines, you can organize your 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 story allocation

If you have multiple teams in a tenant, the tenant owner can provision story limits by team. You can find this option in your tenant admin menu.

### Template

Templates are pre-configured actions or groups of actions that you can use straight out of the box with Tines. You can browse and select them directly from the HTTP request action in the story toolbar. These are also known as public templates.

### Tenant

When you sign up to Tines you and your team are organized together in a shared workspace known as a tenant where you can collaborate together.

### Tenant owner

Tenant owners can perform actions on any object, regardless of the team that owns it (users who are not tenant owners can only perform actions on objects owned by a team they are a member of).

### Throttle Mode

A mode of the event transformation action. 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.

### Tines Bootcamp

A program that provides hand-on guidance for building stories in Tines. It's found [here](https://www.tines.com/bootcamps/).

### Tines Command Runner (TCR)

Powers the run script action on self-hosted infrastructure. It runs Python with access to packages like \`requests\`, \`pandas\`, \`cryptography\`, and \`boto3\`. Adding libraries is straightforward, just point [TCR](https://www.tines.com/docs/self-hosting/additional-applications/run-script-for-self-hosted/) at a public or local PyPI server and install what you need.

### Tines University

A learning resource that can help you to get certified with Tines. It's found [here](https://www.tines.com/university/).

### Tines What's New

The name of Tine's change log, found [here](https://www.tines.com/).

### Tools

In addition to the eight action types available in the menu next to your Storyboard, you can also drag on one of 4 tools. They are; group, page, note, and Record.

## **U**

### User

An individual in Tines with access to view, edit, or manage stories and/or the tenant.

## **V**

### Version

Story-versioning allows teams to back up, inspect, clone, export, and restore any workflow. You can access version history via the clock icon at the top-right corner of your Storyboard.

### Viewer

As a viewer, you have **read-only permission** across a team. You can view, but not edit, objects in a team. This role is 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.

### Value types

Value types are options for creating the payload in certain types of actions (like Send to story and Event transformation. A value can be text, formula, number, true/false, null, an object or an array. [You can learn more here.](https://explained.tines.com/en/articles/7229130-how-to-use-different-value-options-in-tines)

## **W**

### Webhook action type

One of the eight action types in Tines. A webhook enables communication between two applications in real-time. To create a new webhook, simply drag it onto your Storyboard to create a URL which can receive HTTP data.

### Webhook API

Capture and process the responses received from external systems via webhooks.

### Webhook rules

Criteria that must be met in order for an event to be emitted by a Webhook action.

### Workbench

A Tines-powered AI chat interface. When connected to stories, [Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench) can take actions and access proprietary data in real-time, privately and securely. You can interact with this using natural language (NL) or use a template.

### Workbench for Storyboard (formerly Story copilot)

AI-powered chat interface to build, understand, and optimize flows on the storyboard. You can use it to start from scratch or to work with an existing story. [Workbench for Storyboard](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot) makes Tines accessible to the whole team, regardless of skillset.

### YDWWT - You Did What With Tines?

A contest where you can submit a Story that demonstrates the overall concept of a use case handled using the Tines service. It's found [here](https://www.tines.com/you-did-what-with-tines/).

## Thinkst Canary Authentication Guide

Canary devices (hardware, VMWare Images, AWS AMI's or GCP machines) are deployed onto your networks in minutes mimicking high-value targets. External attackers and even malicious insiders tip their hands and reveal their presence by touching them.

## First, get a Thinkst Canary API key

1.  Login to your Thinkst Canary account
    
2.  Click "Settings"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866589605/8c02c3640ae92c862400152c/image.png?expires=1784851200&signature=8ef1a522c09eefee2a18c43b1459fd5804c5ab58d32b10f0168ec3bff1e5b49e&req=fCYhE8F3m4FaFb4V1XW4gavhNTaCoaeuY5PF0%2FlA8EsMdg3mvgrlINEFmrNs%0ASlmOqGBsdZjBSiO4VHUMpYC0AA%3D%3D%0A)
    
      
    ​
    
3.  Click the "API" tab and then the "Add new Global API Key"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866601167/1f0c3afa9859eabf0a03acaf/image.png?expires=1784851200&signature=1656fe54e5035760e69284414151f210527d1135efde7c6f6f140c555991ad6e&req=fCYhEMl%2FnIdYFb4V1XW4gQB3A6%2B1hKhevI7sn3msrRraTkibmJKuxrVb8vo0%0Aiu140dIklF%2BFbmMucvzUUUIqGQ%3D%3D%0A)
    
      
    ​
    
4.  Choose the appropriate key type, name the key and click "Create"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866612215/4a60aa9d750d280ace0cb869/image.png?expires=1784851200&signature=b5a997218c77690b2ca9bca7815790eb46d35875bd7cc580d4a729ceb23be24f&req=fCYhEMh8n4BaFb4V1XW4gR8bTdmV0db7bs3ThmRsDx6b6uFVTuAFNYYaG4k3%0A2VfSOeNhmCY5ASVxMIUjKLH%2BPg%3D%3D%0A)
    
      
    ​
    
5.  Copy the API Key  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/866613493/d1f7271bd9bf249df03e53bc/image.png?expires=1784851200&signature=ce47e0414933276cd0585254d925fdea4c6108d63061cc3dcb77f478b281a317&req=fCYhEMh9mYhcFb4V1XW4gd40zi2THsc%2FrVG%2BXz4gLugM8%2FL0wr%2F6K7eWZisS%0Ajh4jvx4GdbVdmeQrbLTcGaffQA%3D%3D%0A)
    

## Lastly, create a Thinkst Canary credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Thinkst Canary credential
    
    1.  **Name:** Thinkst Canary
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Thinkst Canary** **stories in the [story library](https://www.tines.com/library?s=thinkst%20canary&view=all)**

## "This HTTP Request Action's URL did not match any of this credential's supported domains/URLs." Error

## Overview

When executing an [HTTP Request action](https://www.tines.com/docs/actions/types/http-request/) that requires authentication, you may run into the following error:

```
This HTTP Request Action's URL did not match any of this credential's supported domains/URLs.
```

## Cause and solution

The "`This HTTP Request Action's URL did not match any of this credential's supported domains/URLs.`" error message is directly tied to the **URL** value being passed in the HTTP Request action and the used **[Credential's](https://www.tines.com/docs/credentials/) [URLs and Domains](https://www.tines.com/docs/credentials/credential-configuration/domain-restriction/)** setting.

### Cause one: Incorrect credential usage

The credential that is being used in the HTTP action is not the correct credential to authenticate the HTTP call.

> 🟢 **Solution:** Ensure that you are using the appropriate credential relevant to the HTTP action you are attempting to execute. To avoid this, we recommend naming your credentials so that it is easy for you and your team to identify which platform they are intended for.

### Cause two: Invalid credential URLs and Domains configuration

The credential's **URLs and Domains** setting is formatted incorrectly or contains the wrong values.

> 🟢 **Solution:** Ensure that the URLs, domains, and server hosts passed in the configuration align with the setting's [supported syntax](https://www.tines.com/docs/credentials/credential-configuration/domain-restriction/#supported-restrictions). If you have multiple values, make sure that they are *space-separated* within the **URLs and Domains** setting.

## Troubleshooting checklist

☑️ Verify you are using the correct credential for the HTTP action you are executing.

☑️ Verify that the credential's **URLs and Domains** setting is configured correctly.

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**:

> ℹ️ **General Information:**
>
> -   Describe the behavior in detail, including steps to replicate (*we recommend providing this information via a screen recording*).
>     
> -   The time and date of the reported behavior.
>     
>
> ℹ️ **Story Details:**
>
> -   The impacted story's URL.
>     
> -   The impacted credential's **URLs and Domains** value(s).
>     
> -   The impacted HTTP Request action's:
>     
>     -   **ID:** Click on the action tile within your story; its ID is located at the end of the browser URL.
>         
>     -   **Configuration**: You can copy and paste actions into a code block; this will provide the configuration of the action(s) in JSON format so that Tines Support can view it within our test environments.

## "This page is not ready yet." Error

## Overview

While working with [Pages](https://www.tines.com/docs/pages/), you may run into the following error when trying to access the page URL:

```
This page is not ready yet.
```

## Cause and solution

Within a story flow, if a [page is at the top of a story](https://www.tines.com/docs/pages/distribution-and-access-control/#pages-which-kick-off-story-runs), then it will have a static URL. However, if a [page is located mid-story](https://www.tines.com/docs/pages/distribution-and-access-control/#downstream-mid-story-pages) (i.e., has a connecting action before it), then the page's URL is dynamically generated for each [story run](https://www.tines.com/docs/stories/story-runs/) (this is typically used when referencing the page dynamically in the story via the `PAGE.page_name` variable).

### Cause One: The story run hasn't reached the page yet

If you try to access the dynamic page URL before the story run has reached the page, you will see the "`This page is not ready yet.`" error.

> 🟢 **Solutions:**
>
> -   Check the associated story run for any potential interruptions and correct them.
>     
> -   Confirm that the actions are properly connected to each other within your story.

### Cause Two: The page URL has expired

When working with a dynamic page URL, you can run into the "`This page is not ready yet.`" error if any of the executed events of the story run related to it have been:

-   Manually deleted by a user.
    
-   Deleted due to the event retention of the **Keep events and logs** settings of the story.
    

This causes the instance of the page to expire.

> 🟢 **Solutions:** Check to see if any events were deleted related to the dynamic page URL:
>
> -   **Manually deleted by a user**:
>     
>     -   Validate the upstream events of a page via the **Story runs** option in the three-dot menu of the story.
>         
>     -   If you have [Audit logs](https://www.tines.com/api/audit-logs/) enabled, search for the `ActionsEventsDeletion` operation on the story.
>         
> -   **Story event retention**: Check the **Keep events and logs** setting via the story configuration panel to validate that the timeframe of retained events align with the expiration of the dynamic page URL's creation.
>     
>     ![](https://downloads.intercomcdn.com/i/o/am0g137l/1660695470/c29a3eebd88fb9e618a158168bb4/image.png?expires=1784786400&signature=eeeb7df34760360f229ae6101908b37de0c13d88304f7c5c9a2cc79452bdf462&req=dSYhFs93mIVYWfMW3nq%2BgWTWtJQPN2QzmtGYZa7YjqKtVk6LA2yJopMVmqGw%0AFlufZ3Wf1DuHQ9QCfn5wI8gO9gc%3D%0A)

## Troubleshooting checklist

☑️ Verify that you are using the correct page in your `PAGE.page_name` variable when referencing it in other actions within your story.

☑️ Verify that you are not trying to access the dynamic page URL too early.

☑️ Verify that the events of the story run associated with the dynamic page URL have not been deleted.

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**:

> ℹ️ **General Information:**
>
> -   Describe the behavior in detail, including steps to replicate (*we recommend providing this information via a screen recording*).
>     
> -   The time and date of the reported behavior.
>     
>
> ℹ️ **Story Details:**
>
> -   The impacted story's URL.
>     
> -   The impacted page's action ID (click on the page tile within your story, its ID is located at the end of the browser URL).
>     
> -   The impacted page's URL.

## ThreatQ Authentication Guide

ThreatQ improves the efficiency and effectiveness of existing security operations by fusing disparate data sources, tools, and teams to accelerate threat detection and response.

## First, get ThreatQ OAuth Client Credentials

##### Hosted deployment

1.  Contact [support@threatq.com](mailto:support@threatq.com) to have a ticket created to generate OAuth Client Credentials using the name “Tines"
    

##### On-Premise deployment

1.  SSH into your ThreatQ instance
    
2.  Run `cd /var/www/api`
    
3.  Run `sudo php artisan threatq:oauth2-client --name "Tines"`
    
4.  Copy the credentials to a text editor
    

## Lastly, create a ThreatQ resource and credentials in Tines

##### Resource

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the ThreatQ resource
    
    -   **Name:** ThreatQuotient Domain
        
    -   **Description:** Optional
        
    -   **Builder:** Your ThreatQ instance hostname, i.e. `tenant.threatq.online`
        
5.  **Optional**
    
    -   **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

##### Text type credential

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the ThreatQ credential
    
    1.  **Name:** ThreatQ Client Secret
        
    2.  **Description:** Optional
        
    3.  **Value:** Your ThreatQ OAuth Client Secret
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

##### HTTP request type credential

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the ThreatQ credential
    
    1.  **Name:** ThreatQuotient
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<<RESOURCE.threatquotient_domain>>/api/token?grant_type=client_credentials`
        
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Headers:**
        
        1.  `Accept: application/json`
            
    7.  **Basic auth:**
        
        1.  **Username:** ThreatQ OAuth Client ID
            
        2.  **Password:** `<<CREDENTIAL.threatq_client_secret>>`
            
    8.  Click "Run options", navigate to the `access_token` key and double click on the key
        
    9.  **Location of token from response:** Paste the path to the token in the value pill, `<<threatquotient.body.access_token>>`
        
    10.  **TTL:** 3599
         
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/889026595/5393cec823fa57e7a6aabac0/image.png?expires=1784851200&signature=7e752cc47be42ded3c2b7d35587325bb913fb1590f1beb18be49457e6720d16c&req=fCguFst4mIhaFb4V1XW4gfbJkTe04OpOTi65cE%2F6gnj0W0GLtEs8lBhBtXDB%0ART9jdEDORkVJWWTi0sQVcaSpKw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/889026738/66f4e5f13035c20c0aaa4e02/image.png?expires=1784851200&signature=ca89241c22af58b4191b7a7f1e6c82fecb63532b20d438ea3cb92727db8a2615&req=fCguFst4moJXFb4V1XW4gXhmROUIGXE2hb9xVrythohBR%2BlwgGaHIAiVmDlM%0AylViwzKB58XJp%2FOH%2FDYB6nvrAQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/889026913/1c7dd5bf742c5874d9036492/image.png?expires=1784851200&signature=bb7a4252f21c945cae1bb89d07c7f91d90e4f34f0bdb1330303939932b995c84&req=fCguFst4lIBcFb4V1XW4geEkBmwmj%2FgzXQgTL9NR2A0elWBsX8w5tAqSpwmo%0AzsJQ4t5n9uT1VBXk6CfnQ9Vi%2FQ%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of ThreatQ stories in the [story library](https://www.tines.com/library?view=all&s=threatq).**

## Tines API 504 status error: "Net::ReadTimeout with #<TCPSocket:(closed)>"

## Overview

When you [create an API workflow in Tines](https://www.tines.com/docs/stories/apis), you may run into the following error: `"connection_error": "Net::ReadTimeout with #<TCPSocket:(closed)>"`. This article covers common causes and solutions to this behavior.

## 504 Status Error: Cause and solution

### Cause One: "Enable webhook API responses" is not configured properly

If your story's "Enable webhook API responses" setting is not enabled or configured correctly, this error can occur.

> 🟢 **Solution**:
>
> -   Turn on "Enable webhook API responses" in your story configuration:
>     
>
> ![](https://downloads.intercomcdn.com/i/o/1003486680/0d822854a1f1dfb4f058cd11/image.png?expires=1784851200&signature=d78f8316f78abadc1491528494c88fe3756a08a6c9e71ae9f309d126ad66a64c&req=dSAnFc12m4dXWfMW3nq%2BgfEmtav%2Fxim2uPRklNl4d6GMHsoMurQT2tb1BX2V%0AbsThsZGLlwAeAX0oH3boiw8EjeI%3D%0A)
>
> -   Configure entry and exit actions for your webhook API responses:  
>     ​
>     
>     ![](https://downloads.intercomcdn.com/i/o/1003487431/a68d5a6bda6830da7e146d90/image.png?expires=1784851200&signature=9873fa5cd8a4e7ee988e8f5fd0f9da6d71c2879b889ce074f7112a04e5086966&req=dSAnFc12moVcWPMW3nq%2BgbMiVsBr9sIN0FIbsWCf3UCR1ZpENbzQCc%2BFSB9m%0ATsehFP1oiO9PrIzOKXdcR%2Bq%2FwtA%3D%0A)

### Cause Two: The HTTP request timed out

The 504 status error can occur when the HTTP request times out before it is completed.

> 🟢 **Solution**: Increase the "Timeout" setting of your HTTP action (default is 30 seconds):
>
> ![](https://downloads.intercomcdn.com/i/o/1003490931/fa0ca419418ee8d6ab44c3c8/image.png?expires=1784851200&signature=48154c2dcd1e1423a4ea346a0d3856d36f32ca227f8f6b3f601f6c477402a9a1&req=dSAnFc13nYhcWPMW3nq%2BgTc5xSdSNnbGrP%2FnM0HL%2FwxaTQU%2BKbc6yPBc2hIy%0Afmm%2F3o9vOtg8z66M7cjvDA7I6Jg%3D%0A)

## 504 Status Error: Troubleshooting checklist

☑️ Confirm "Enable webhook API responses" is enabled for your story (see [Cause One](#h_9f96227821)).

☑️ Confirm Entry and Exit actions are configured in your "Enable webhook API responses" setting. (see [Cause One)](#h_9f96227821).

☑️ Configure timeout settings in your HTTP Request action (see [Cause Two](#h_012d3fc713)).

## Need further assistance?

Have questions or still seeing the error after working through the sections above? We are happy to help!

⬇️ [Contact Tines Support](https://www.tines.com/contact#support) **with the information below**, when available:

> ℹ️ **General Information:**
>
> -   Describe the behavior in detail, including steps to replicate (*we recommend providing this information via a screen prerecording*).
>     
> -   Date and time of the reported behavior.
>     
>
> ℹ️ **Story Details:**
>
> -   Impacted Story's URL (include all URLs if there are multiple stories impacted).
>     
> -   Impacted Action's ID (include all IDs if there are multiple actions impacted).
>     
> -   The action configuration(s):
>     
>     -   You can copy + paste actions into a code block; this will provide the configuration of the action(s) in a JSON format so that Tines Support can use it into our test environments.

## Tines Tunnel FAQs

## Do I need to open inbound ports to use the Tines tunnel?

No, you do not need to open any inbound ports on your firewall to use the Tines tunnel. All connections from the Tunnel are outbound and initiated by the Tunnel.

## Is there a limit to the number of tunnels that can be used with Tines?

There is no limit to the number of tunnels you can use with Tines, but there is an additional cost for each tunnel.

## What does the number signify next to the tunnel status on the Tunnel Configurations page?

This number represents the total active client connections that are currently routed through the tunnel between the container and Cloudflare's edge proxy. Each container can maintain up to four active connections, but this can fluctuate during retries, for example, temporarily increasing the count. Fluctuations are normal and can be caused by things like:

-   Cloudflare maintenance or updates.
    
-   Network disruptions, causing reconnection attempts.
    
-   Changes in traffic volume, leading to more connections.
    

> **👉 For more details on the Tines Tunnel [see here](https://explained.tines.com/en/articles/6883952-deploying-and-using-the-tines-tunnel).**

## Tines Workbench demo recording

[Watch video](https://fast.wistia.net/embed/iframe/1pxrphrbql)

### Video transcript

Hi. I'm Rosie, product manager at Tines, and I'm very excited to introduce you to Tines Workbench, our AI powered chat interface.

If you're familiar with or you've tried out other AI powered chat tools in the past, you know that they have a really powerful and impressive general knowledge capability.

Workbench has this general knowledge capability, but on top of that, it's actually able to connect to your proprietary systems in real time, send you real time data about them, and even take actions for you.

And similar to all of our other AI offerings and times, your data is going to be kept within your tenant and it's going to be private. Let's look at an example to see Workbench in action. Say you received an alert that an AWS bucket was made public by a user. You could probably go to any AI chat tool and ask it, what do I do if an AWS bucket let's give it the name too, times demo twenty four, was made public by a user.

And any AI chat tool will probably tell you, the context of what that means and maybe a series of steps that you should then take in that tool, for how to solve this problem. And you'll see that we have that here. It's a potential security risk.

First, we should verify the current status, but Workbench is now going one step further with the ability to access your AWS instance already. So it's it's suggesting we run this AWS CLI command.

Let's go ahead and confirm that. And now it's gonna go ahead and in real time, I don't have to write the CLI command. I don't need to log into my AWS.

It's going to actually access my AWS and get the real time status of this bucket.

It looks like the bucket isn't currently public. Looks like the access control shows that the owner has full control over the bucket, but Workbench is going to check the bucket policy to be sure. Similar to that last time, we can check the CLI command. I'm gonna confirm that, and now it's going to check this. So what this is is actually a template that I've enabled on the left hand side here. These templates are provided out of the box by times, and then you're able to enable them and connect them to your system and even select which actions of these templates you would like Workbench to have access to run or do.

It looks like we've heard back from Workbench. The bucket policy reveals it is indeed public, so it looks like our alert is still relevant and that it recommends we remove the access immediately.

Before I do that, maybe I want to take a look at who this user is. Maybe they had a reason for making it public. So let's switch gears and say, who is Steven O'Brien?

We were just looking at our AWS resource information in real time. Now we're going to be looking into our various systems, to find details about Stephen O'Brien.

This lookup user, you'll notice, doesn't actually have any sort of product associated with it. That's because it's a story. So while the templates are provided out of the box by times, you can build as many custom stories for Workbench to call and run as you'd like. Maybe you have specific custom steps you'd like to take.

Maybe it's more than just an individual action template, and that is, fully supported by Workbench here on the stories tab too. So it looks like we have some frame information from Steven. We checked Okta. We checked Tynes.

We checked Slack for this data. And based on this, it actually looks like his last activity was in August and it's now September. So let's actually check in BambooHR, that's our HR system, if Steven is out of office.

If he hasn't been online since August and it's now September and he's currently making changes to AWS bucket policies, he might that that sounds a little odd. Right? So let's go ahead and look at who's out. It's going to get a list of who's out from BambooHR.

Go ahead and run that for me, and it looks like Steven is in fact out of office for the whole month of September. So this now seems like Steven's account may be potentially compromised or he's on vacation making bucket changes, which probably isn't likely. So I think his account has been compromised.

Can you reset his active sessions and password?

Now we're going to go ahead and take action, reset Steven's account password and active sessions in Okta. Once again, calling a story that I created here and we have confirmation that the password has been generated and reset with some suggested next steps. Yeah. But I actually wanna go back now that we've handled the user, part of this and maybe go ahead and make that change back for the bucket to be private again. Right? So can you make that bucket private?

Now I'm being kind of intentionally vague just to showcase that I don't need to restate the bucket's name. It has full context of our conversation here. So it knows the bucket is times demo twenty four. It knows that it was public from our prime previous messages, and now it's going to remove any public access settings first. Great. Once again, we could view that CLI command like we did previously.

It once it runs that, we're gonna get some data back.

Awesome. Now we're going to remove the policy that allows public access.

Perfect. And once again, you know, I'm being supported by Workbench, but I'm still driving this. I'm deciding that I wanna take this action on my AWS bucket.

You know, there there isn't that potential issue of Workbench going rogue or something and and taking actions that I haven't enabled or allowed it to or confirmed.

Great. Now let's verify that the bucket is private by checking its policy. So we've taken actions, and now Workbench recommends let's go and actually pull the status in real time again, check that those actions did in fact successfully take on our bucket, and once again, we have confirmed there's no bucket policy, that's what we wanted, And then we're gonna check the ACL.

And we have a nice summary of our perfectly private bucket now. So in the span of just a couple of minutes, we've taken a number of actions. We've gotten information in real time about AWS buckets. We've then actually gone ahead and changed to the bucket status and policies themselves and gotten confirmation that those changes have taken place.

We've looked up information on a user. We've also then reset their password and sessions, all within this one chat that has all my context. Maybe I want to give a nice summary. So summarize this info and update the case.

And what was my case? Three seven five two four in a comment.

So I'm gonna update this case comment in times. If you wanted to make a new case in another system or send an email or send a message in Teams or Slack or something, that is also possible. So this is just one example of many for summarizing or updating, but here is our nice summary here summarizing all that we've done. And then now we're going to add that information as a comment to that case. That's perfect. And so it's going to once again call that times template update a case.

Now looking here, let's just check. So this looks like it's gonna update the description. I'm actually gonna say no, make it a comment because I just want comment rather than a full description update. So once again, you can check that and then if you don't confirm, it's not going to do it.

And now we will update it as a comment.

Great. Looks like we're commenting on the case. Let's confirm that and then go ahead and check that out.

And we have this nice security incident update here on the bucket, on Steven's account, on any next steps.

There's so much more we could do and so many more use cases that we could dive into, but I really appreciate you tuning in to just this quick look into Workbench. Thank you so much.

## Traceable.ai Authentication Guide

Traceable AI combines the power of end-to-end distributed tracing, cloud-native integrations, and advanced ML-powered behavioral analytics to deliver API and application security from user to code. Traceable's platform works in the background even as application architectures change, development methodologies evolve, and new attack vectors and threats emerge.

### First, get a Traceable AI API token

1.  Login to your Traceable AI account at [https://auth.traceable.ai/login](https://auth.traceable.ai/login)
    
2.  From your account menu, select "My Preferences"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852338257/b4c9af94f7e70b7f8e7c20df/image.png?expires=1784851200&signature=e7226f023f2905c1334e8391a9bc5823daa946a2e6eab9f3d802cd528b777cdb&req=fCUlFcp2n4RYFb4V1XW4gQ069hsQGSbtRWKV4kt1D%2BnKDauqMXFHT5%2FL8JuK%0AagLmiMzWr2shwhk3%2FwX8jICJ%2Fg%3D%3D%0A)
    
      
    ​
    
3.  Click on the "API Tokens" tab and click "Generate API Token"
    
4.  Name the token and click "Generate"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852338629/f294bc76885a3e21023157af/image.png?expires=1784851200&signature=ed70daad86109f9c8b2b3663723f0a52f0430d2b5c95d672f448d66617d8a583&req=fCUlFcp2m4NWFb4V1XW4gfa9vSWkP%2FTy8V3GFeuqy8wluj%2FjBG817WqlR6Z6%0AHBCxSc%2B2fdywWMyjD70G9H9jrw%3D%3D%0A)
    
      
    ​
    
5.  Copy your token and click "Okay"
    

### Lastly, create a Traceable AI credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Traceable AI credential
    
    1.  **Name:** Traceable AI
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Traceable AI** **stories in the [story library](https://www.tines.com/library?s=traceableai&view=all)**

  
​

## Trello Authentication Guide

Trello is the visual tool that empowers your team to manage any type of project, workflow, or task tracking. Add files, checklists, or even automation: Customize it all for how your team works best

### First, get a Trello API key

1.  Login to your Trello account at [https://trello.com](https://trello.com)
    
2.  Navigate to [https://trello.com/power-ups/admin/](https://trello.com/power-ups/admin/) and click "New"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852357211/c9116d4e28840c1696d68ec9/image.png?expires=1784851200&signature=2089458af3bb3358a803ced44557085009e8c0752d48db14a6a9978f377fedd4&req=fCUlFcx5n4BeFb4V1XW4gfLttl78oWqS6gpDQBgUrOozN8LLTmtmx8SSChlw%0AuHH%2FlibPMwXaPQuyvhSO1vpDXA%3D%3D%0A)
    
3.  Name the integration, choose a Trello workspace, add your email address, and click "Save"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852357977/db180170f639b04d987eda01/image.png?expires=1784851200&signature=7cb46a154b605fe458d5d48c1e58342ef854c95ec91c0a12110f8e0e316b1b35&req=fCUlFcx5lIZYFb4V1XW4gWJQNGTaaFyO3kJAFl%2FwjyM9IoUS7uxyI%2BnO5vLR%0ARbFxYmyMSq%2FCXpG1nJTz%2BZTiyA%3D%3D%0A)
    
4.  Click on "API key" and click "Generate a new API key"  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852358498/1a78a9295f42fda3dae75d8c/image.png?expires=1784851200&signature=4e668beec141653002906268f142a64e47a93ad202b8a24e55f91755b1f376e2&req=fCUlFcx2mYhXFb4V1XW4gYnqyg8feeYXzmMw%2BOmK2sTPi9oywCr7K8MFerf0%0AyUZZWD14J%2F775VDSYdUJljJcHA%3D%3D%0A)
    
5.  Copy your "API key" to a text editor
    
6.  Click on the "Token" link to generate a token  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852360011/f41260eb911244f2dc54ac7f/image.png?expires=1784851200&signature=9d5763aa6705990ac87709009ebcb6386a46719ceab6e7e35a6ca37c62ad44fa&req=fCUlFc9%2BnYBeFb4V1XW4gSz8wzv0HHfpoXpvhYCtNN84H4YDisxozj3J6UTk%0Akcb24RG8z%2BkWc1leYxAP6UukQw%3D%3D%0A)
    
7.  On the authorization page, scroll down and click "ALLOW".
    
8.  Copy the token to a text editor  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/852361293/3716fa6b33aa6468c5ab7f77/image.png?expires=1784851200&signature=d05163f79f8e07f600b4f253d1f5293f8003425af06c84fdcba8f934d3a40767&req=fCUlFc9%2Fn4hcFb4V1XW4gSVjDnTHoUvFMcpJqwsHSpPcTL%2F9VPwHAHvbvUV0%0A9KQ7hjOhzqksdRZhEabiR0oU5w%3D%3D%0A)
    

### Lastly, create a Trello credential in Tines

**Trello connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Trello" and follow the prompts to connect.
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/trello).**
>
> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Trello** **stories in the [story library](https://www.tines.com/library?s=trello&view=all)**

## Troubleshooting guide - UI latency diagnostics

This guide is designed for technical administrators and users to identify the root cause of UI latency, confirm system health, and perform self-service diagnostics before escalating to support.

## Step 1. Verify backend system health

Before diagnosing local issues, confirm that the platform itself is fully operational.

-   **Check platform status:** Navigate to Tines’ dedicated status page here - [https://status.tines.com/](https://status.tines.com/).
    
-   **Action:** Confirm that all core backend systems are reporting as operational and that there are no known incidents or periods of degraded performance.
    

If the status page indicates an ongoing issue, no further troubleshooting is required; wait for the platform to recover. If the status is operational, proceed to step 2.

## Step 2. Client-side and network diagnostics

The majority of reported performance issues stem from client-side environment factors, such as network settings or browser configuration.

### Network environment checks

These checks help isolate whether the latency is related to your connectivity.

-   **Test connectivity:** If possible, test the UI performance on a different network (e.g., a private connection versus a corporate one). If the issue is isolated to your current network, contact your local network administrator or internet service provider (ISP) for assistance.
    
-   **Isolate VPN interference:** If you are using a virtual private network (VPN), temporarily disable or remove the VPN and retest the UI performance. A significant improvement in performance indicates that the VPN configuration is the likely cause of the latency.
    
-   **Check bandwidth saturation:** Ensure that other applications (e.g., streaming, large file downloads, or aggressive torrent clients) are not consuming all available bandwidth, which can prevent the UI from resolving pages quickly.
    

### Browser environment checks

Browser configuration and accumulated data can severely impact UI responsiveness.

-   **Use incognito/private mode:** Open a new incognito or private browsing window and reproduce the issue. This minimizes interference from existing login sessions, stored cached data, and browser extensions, offering a cleaner environment for testing.
    
-   **Disable extensions:** Temporarily disable all non-essential browser extensions, particularly security tools, ad blockers, or any extensions that modify page content or interfere with network requests.
    
-   **Clear browser cache:** Clear the browser's disk cache and cookies. In chrome, a good practice is to open developer tools and perform an empty cache and hard reload.
    
-   **Check for stable version:** Verify that you are running a stable and up-to-date version of your web browser.
    
-   **Disable hardware acceleration:** If operational lag persists, navigate to your browser's settings and temporarily turn off hardware acceleration. Relaunch the browser and retest.
    

## Preparing for support escalation

If the UI latency issue persists after completing all checks in step 2, you may be experiencing a genuine platform issue. Gather the following diagnostic data before submitting a ticket.

-   **Reproduce and capture:** reproduce the latency issue reliably while recording network traffic.
    
-   **Capture a HAR file:** generate an HTTP archive (HAR) file. This file captures all network requests and responses, providing support with the data needed to pinpoint where the performance delay is occurring (client-side versus server-side).
    

### Escalation criteria

Open a support ticket only after you have:

-   Confirmed the platform status page shows operational.
    
-   Completed all diagnostics in step 2 without resolving the issue.
    
-   Confirmed the issue is reproducible by other users in different client environments (different networks or devices).
    

When submitting the ticket, include the following information:

-   The captured **HAR** file.
    
-   A clear description of the issue, detailing what you expected to happen versus what actually happened.
    
-   The exact troubleshooting steps you performed from this guide.
    
-   The exact date and time (including your timezone) when the issue occurred.
    

### Contact support

With all the information gathered, please contact [support](https://www.tines.com/contact/#support) to raise an issue.

## Understanding Tines pricing and packaging

## **Your journey to intelligent workflows starts here.**

At Tines, we've designed our product packages to grow with you at every stage of your intelligent workflow journey. Whether you're just beginning to explore automation or scaling intelligent workflows across your enterprise, we offer the perfect solution tailored to your organization's unique needs and maturity level.

### Simple, transparent, and powerful

We believe powerful technology should come with straightforward pricing. Our intuitive model is designed to provide maximum value while eliminating complexity:

-   Strategic feature sets: Access the capabilities you need based on your current workflow maturity and business objectives
    
-   Generous usage allocations: Enjoy ample capacity for stories, teams, users, and daily events that scale with your selected tier
    
-   Flexible enhancement options: Customize your experience with targeted add-ons that extend capabilities or increase usage limits as your needs evolve
    

> **Note**: On June 25th, 2025, we introduced enhanced packaging options for new customers. These improvements are available to all existing customers as well. For details specific to your account, please connect with your dedicated Tines account manager.

* * *

## Choose your path to success

**Community Edition |** *Learn how to automate*

Our forever-free version gives you hands-on experience with Tines' powerful capabilities. Perfect for proof-of-concepts, learning, and building your business case for broader implementation. Experience the platform's intuitive design with select features and usage parameters.

**Business Edition |** *Scale your workflow program*

Everything ambitious teams need to transform their operations through intelligent workflows. This comprehensive edition provides the essential tools, capabilities, and capacity to establish and grow a successful workflow program that delivers measurable business impact.

**Enterprise Edition |** *Transform your organization*

Our enterprise edition includes our complete suite of enterprise-grade features, expanded usage limits, and a dedicated environment. Designed for organizations implementing intelligent workflows at scale across multiple departments with the highest levels of security, control, and support.

> Both Business and Enterprise Editions are available for self-hosting.

### **Edition comparison**

Below is an overview of the difference between our community edition and paid editions of Tines.

| **Feature bucket** | **Community Edition** | **Business Edition** | **Enterprise Edition** |
| --- | --- | --- | --- |
| Workflow Essentials | ✔ | ✔ | ✔ |
| Workflow Controls | ✔ | ✔ | ✔ |
| Security Essentials | SSO only | ✔ | ✔ |
| Team Essentials |  | ✔ | ✔ |
| Advanced Workflows |  | ✔ | ✔ |
| Enterprise Tenant Management |  |  | ✔ |
| Dedicated Cloud Tenant |  |  | ✔ |

*✔ indicates that the bundle unlocks in this edition, please speak to our team for specifics.*

**Available add-ons**

Increase the capacity of your current plan without moving to a new level.

| **Add-ons** | **Community Edition** | **Business Edition** | **Enterprise Edition** |
| --- | --- | --- | --- |
| Cases |  | ✔ | ✔ |
| [AI run-time credits](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits) |  | ✔ | ✔ |
| Tunnels |  | ✔ | ✔ |
| Tenants |  | ✔ | ✔ |
| [Flows](https://explained.tines.com/en/articles/10860654-what-is-a-flow-in-tines) |  | ✔ | ✔ |
| Teams |  | ✔ | ✔ |
| Apps |  | ✔ | ✔ |

**Usage limits**

Generous usage limits are set across our editions.

| **Usage** | **Community Edition** | **Business Edition** | **Enterprise Edition** |
| --- | --- | --- | --- |
| Flow limits | 3 | Starting at 30 | Contact Tines |
| Licensed teams | 1 | Starting at 1 | Contact Tines |
| User limits | 1 | 100 | Contact Tines |
| Event limits | 25,000 monthly | 1.5M daily | Contact Tines |
| AI run-time credits (monthly) | 50 | Contact Tines | Contact Tines |
| Apps |  | 3 | 5 |

## Feature sets designed for your success

Our thoughtfully organized feature sets bring together powerful capabilities that maximize value for your needs. As we continuously expand the Tines platform, these feature sets grow to deliver even more possibilities.

Each set is carefully defined to provide clarity and predictability, helping you understand exactly what you're getting today and what enhancements you can expect tomorrow. We're committed to maintaining a clear structure that makes it easy to identify which Tines plan delivers the right combination of capabilities for your organization.

Our customer-focused approach means we only introduce new feature sets when they represent truly transformative capabilities that deserve special attention. This ensures you always have access to a clear, understandable path to the workflow solution you need.

The list below is accurate as of: 1 May 2026

| **Feature set** | **Definition** | **Example features** |
| --- | --- | --- |
| Workflow Essentials | A robust feature set to build, run, and monitor your most important workflows. | 
-   Reporting
-   Pages
-   Send To Story
-   Action Templates
-   Notifications
-   Resources
-   Credentials
-   **AI Agent action**
-   **Workbench**
-   **Workbench for Storyboard**

 |
| Workflow Controls | Ensure your most important workflows are running as expected. | 

-   Scheduling
-   Monitoring
-   Story locking
-   Smart retries
-   Version History
-   Custom Tenant Name

 |
| Security Essentials | The essential security features to protect your most important workflows. | 

-   SSO, all plans
-   Audit logs
-   Tines Roles

 |
| Team Essentials | Everything multiple teams need to collaborate, work on, and manage workflows together. | 

-   JIT Provisioning
-   Custom Object Sharing
-   Change Control
-   Private Templates
-   Saved story runs

 |
| Advanced Workflows | Beyond process automation, do more with your workflows with these advanced cross-story capabilities. | 

-   Records
-   Dashboards
-   Dashboard manager role
-   Workflow as API
-   Collections
-   SSO group support for Pages
-   Pages & prompts: custom domains

 |
| Enterprise Tenant Management | The feature requirements for an enterprise organization to build, run, and monitor their most important workflows. | 

-   Custom Roles (RBAC)
-   Custom Email Senders (5)
-   Priority Processing
-   SCIM Provisioning

 |
| Dedicated Infrastructure | This moves you to a dedicated infrastructure which gives you an isolated environment which gives you greater control and customization options. | 

-   Priority processing
-   Custom domain
-   Allow-listing incoming IPs

 |

### Historic pricing models

If you moved to a paid edition before June 25, 2025, you are most likely on one of our older pricing models. While similar, there could be core differences. For more information on your pricing model, please reach out to your Tines contact and we’ll be happy to help.

### Talk to a product expert

What to expect:

-   Zero-pressure, technical demo of the Tines platform
    
-   Walkthrough of a relevant use case
    
-   Overview of products and plans
    
-   Insight into API and integration capabilities
    

[Let us show you Tines](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained)

## Updates to the assigned IP address for Tines cloud tenants coming on August 16th.

We are changing the assigned IP addresses for your Tines cloud tenant.

### What are we changing?

We are adding a second IP address to your Tines tenant. There will be no interruption to service while making this change.

### Why are we doing this?

This is to increase the performance and redundancy of your Tines cloud tenant.

### When are we making this change?

This will be implemented on August 16th, 2023. If there are any updates, this article will be updated, and a message will be sent to all users.

### How do I know what my IP addresses are?

To see the egress IP addresses for your tenant, visit `https://<tenant-domain>/info`

### What do I, the Tines user and administrator, need to do to avoid disruption?

If you have the existing IP address of your Tines tenant to an allowlist, you will need to add the new IP address to it, also.

> If you require assistance, please contact [support@tines.com](mailto:support@tines.com) or from within Tines, as shown below.
>
> ![](https://downloads.intercomcdn.com/i/o/786739203/0248cf8de1012ac68a23c5a2/Screenshot+2023-07-17+at+14.48.32.png?expires=1784851200&signature=9842320d6b3fe2199cfb4dad69eb55ab542f9b6d92469fc9ad972be3a3dbfe8f&req=cyghEcp3n4FcFb4V1XW4gaCbsyoVr7UqquStpnmMNx3fh2SmOn3dz5HBALJv%0Abt3NcHefQxsAkjzM30hkaZZgTA%3D%3D%0A)

## urlscan.io Authentication Guide

[urlscan.io](https://urlscan.io/) is a free service to scan and analyze websites. Here's our guide for authenticating urlscan using Tines.

## First, get a urlscan API key:

1.  Navigate to [www.urlscan.io](http://www.urlscan.io) and click "Login", New urlscan users can create a new account via the "Create one!" link
    
    ![](https://downloads.intercomcdn.com/i/o/750315542/9fabec39bf7c82f448a5f716/Screen+Shot+2023-03-27+at+4.41.03+PM.png?expires=1784851200&signature=970a4df94d1790532314cffc27416111948654220bde3dcea39bae2b30e6dfdc&req=cyUnFch7mIVdFb4V1XW4gT1wZ5wndchAW7FyEV6eYHlKAmabfqIxs%2FX076vd%0AiVKgyPBpxXz2vlkJ%2Bq4nbUY5kg%3D%3D%0A)
    
2.  On the left-hand side, click "Settings & API"
    
    ![](https://downloads.intercomcdn.com/i/o/750315769/ef344ee1465d44f8ddb9a195/Screen+Shot+2023-03-27+at+4.42.04+PM.png?expires=1784851200&signature=f8454a33734415dae3147afdf869e4e77880d6b5ef736c6c88577e5fda8a0c72&req=cyUnFch7modWFb4V1XW4gdiras0Cf1RPMzWu14m%2Fw6MnTbwMD%2BPz0ECb5F%2BH%0AB8avO03y9PiLzuvzOUSbUKNf%2Fg%3D%3D%0A)
    
3.  Click "Create New API key"
    
    ![](https://downloads.intercomcdn.com/i/o/750315946/9f5be69c20288a7ef5754221/Screen+Shot+2023-03-27+at+4.42.55+PM.png?expires=1784851200&signature=ba4f0d5d8d7e59984d8dd1e00d576dab6e778aebe6d62e35f8013dccc719ad00&req=cyUnFch7lIVZFb4V1XW4gVuRBgcLYI%2BbMxsv4jgJPaiIph6V2wUn6b81QwiC%0AHmzGNxUM4Wppoq8RnyDWiZ3J3g%3D%3D%0A)
    

5\. Copy the API key.

## Lastly, create a urlscan credential in Tines

**URLScan.io connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "URLScan.io" and follow the prompts to connect.
    

**Manual credential creation**

This method is not recommended as the URLScan.io connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure that this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Learn more about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/urlscan-io).
>
> For more on creating credentials in Tines click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of urlscan.io stories in the [story library](https://www.tines.com/story-library?s=urlscan)

## Use a preferred AI provider in Tines

When you create a tenant in Tines, you automatically gain access to AI capabilities in Tines features and Workbench. Tines offers models from both AWS Bedrock and OpenAI but you can choose to use a different LLM.

## Getting started

Before bringing in your preferred AI provider into Tines, you must first understand your organization’s AI infrastructure.

**Consider the following questions:**

-   What AI models does your company support?
    
-   Are the models schema-compatible (input and output) with either Anthropic or OpenAI?
    
    -   *A model may not be schema-compatible if your company wrote its own API or hosts a model other than Claude, OpenAI, or Ollama.*
        
-   Is your Tines tenant on the cloud or self-hosted?
    

### Available providers

Tines lets you connect with the following AI providers, configurable via the below customizations:

| **Provider** | **Base URL** | **API key** | **Custom models** | **Custom headers** |
| --- | --- | --- | --- | --- |
| Anthropic | ✅ | ✅ | ✅ | ✅ |
| OpenAI | ✅ | ✅ | ✅ | ✅ |
| AWS Bedrock | ✅ | ✅ | ✅ | ✅ |
| Mistral | ✅ | ✅ | ✅ | ✅ |

> Notes:
>
> 1.  While we support the above providers directly, we’re able to support any OpenAI or Anthropic spec-compatible providers or observability tools. Refer to the [AI Admin docs](https://tines.com/docs/admin/ai/) for the most current list. As with all features, this list will continue to evolve.
>     
> 2.  Each model running locally will have to expose an OpenAI compatible API so that Tines may connect to it. Systems running on Ollama already provide this.

## Add your own provider

Tenant admins can follow these steps to add your own AI API key:

1.  Navigate to **Settings** - **AI settings**
    
2.  Toggle on **Use custom provider**
    
3.  **Choose the provider**
    
4.  **Enter the API key**
    

[Watch video](https://fast.wistia.net/embed/iframe/oz5ohm7c7a)

### **Scope a provider to specific teams**

By default, all teams in your tenant have access to every configured AI provider. Tenant owners can now restrict access to specific teams, giving you more granular control over which teams can use a given provider and its models.

To scope a provider to specific teams:

1.  Navigate to Settings → AI settings
    
2.  Select the provider you want to configure
    
3.  Under Access, deselect "Grant access to all teams on this tenant"
    
4.  Update the specific teams that should have access
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2269215454/d4064c83fcb3c4f987a52c7177d8/team+level+ai+access.gif?expires=1784786400&signature=cd94e5ad5ad601404fc7a6b20dcbb2ee0e7eacde443a74cb3a585882cde504de&req=diIhH8t%2FmIVaXfMW3nq%2Bgfr%2FoSOYRrmIpITZHyoKQWwxpCMqmJQPea0UjGbs%0Ajuy1iFghgvwdlveIgluP9RldHnk%3D%0A)

The team that owns the provider's credential will always retain access. This is useful for managing costs, enforcing compliance requirements, or gradually rolling out a new model to specific teams first.

## Additional resources

-   Explained
    
    -   [AI in Tines Explained articles](https://explained.tines.com/en/collections/9473753-ai-in-tines)
        
        -   [How to pitch AI in Tines to your GRC team](https://explained.tines.com/en/articles/10389755-how-to-pitch-ai-in-tines-to-your-grc-team)
            
        -   [Frequently asked questions: preferred AI providers](https://explained.tines.com/en/articles/10389743-frequently-asked-questions-custom-ai-providers)
            
        -   [How do I turn on or off the AI-powered features](https://explained.tines.com/en/articles/9396326-how-do-i-turn-on-or-off-the-ai-powered-features)
            
    -   [Workbench Explained articles](https://explained.tines.com/en/collections/10398899-workbench)
        
-   Library story
    
    -   [Compare prompts across LLM models in Openrouter](https://www.tines.com/library/stories/1281993/?name=compare-prompts-across-llm-models-in-openrouter)
        
-   Docs
    
    -   [AI Agent action docs](https://www.tines.com/docs/actions/types/ai-action/)
        
    -   [Automatic mode in Event Transform docs](https://www.tines.com/docs/actions/types/event-transformation/automatic/)
        
    -   [Workbench docs](https://www.tines.com/docs/workbench/)
        
    -   [Admin - AI docs](https://tines.com/docs/admin/ai/)
        
-   Blogs
    
    -   [AI on your terms: introducing preferred AI providers in Tines](https://www.tines.com/blog/preferred-ai-providers)
        
    -   [Securely bringing your own AI to Tines](https://tines.com/blog/securely-bringing-your-own-ai-to-tines/) by Matt Muller

## User administration

If you're a tenant owner, you can invite and manage additional users for your tenant. There are two levels of user access: user and tenant owner.

In addition to tenant-level access, tenant owners can provision more granular access at the team level. Users must be added to a team in order for them to collaborate on Stories with other users. [Learn about teams and permissions in our article here](https://intercom.help/tines-9c531f867f99/en/articles/6883947-using-teams-in-tines).

### **About tenant owners**

Tenant owners have ultimate authority over the tenant. This includes:

-   Provisioning user access
    
-   Removing users from a tenant
    
-   Deleting queued or retrying jobs
    
-   Pushing changes to live in change control
    
-   Manage object sharing permissions
    

### **User Management**

To manage users, follow these steps.

1.  **View a user.** From the menu beside your team name, select "Users" ([`https://<tenant-domain>/admin/users`](https://\<tenant-domain\>/admin/users)). This opens a window with a list of users, teams and roles, and last seen time.
    
2.  **Edit a user.** Click on the user's name to view more information and edit their profile.
    
3.  **Invite a user.** Click "New user" from the window and input the users' information. Click "Save" and an invite email will send with instructions on how to complete their registration.
    

### **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.

> **👉 Visit the Teams [page](https://intercom.help/tines-9c531f867f99/en/articles/6883947-using-teams-in-tines) for more information on team user roles.**

### **How do I make a user an admin in Tines?**

From the users menu in your settings, click on the user's profile. From here, you'll be able to update their details, including their role.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1810212590/abc8913d35bbe2f7f42369b11efd/CleanShot+2025-11-03+at+10_31_32%402x.png?expires=1784851200&signature=f87d856129aaa4df7435ec600cf7e7fdd869f9683f490dcf100e7fa38ce4c516&req=dSgmFst%2Fn4RWWfMW3nq%2BgZCW%2BAmxSAiJXcTonhM1Sl0hUx5zYdnvn0pnuOTr%0AiMTzSe7WU9zlt213MuZLLSkXdHw%3D%0A)

## User administration in Tines

If you're a tenant owner, you can invite and manage additional uses for your tenant. There are two levels of user access: user and tenant owner.

In addition to tenant-level access, tenant owners can provision more granular access at the team level. Users must be added to a team in order for them to collaborate on Stories with other users. [Learn about teams and permissions in our article here](https://intercom.help/tines-9c531f867f99/en/articles/6883947-using-teams-in-tines).

#### About tenant owners

Tenant owners have ultimate authority over the tenant. This includes:

-   Provisioning user access
    
-   Removing users from a tenant
    
-   Deleting queued or retrying jobs
    
-   Pushing changes to live in change control
    
-   Manage object sharing permissions
    

### User Management

To manage users, follow these steps.

1.  **View a user.** From the menu beside your username, select "Manage users" (`https://<tenant-domain>/admin/users`). This opens a window with a list of users, their current status, tenant role, and last active time.
    
2.  **Edit a user.** Click "Edit user" from the "Actions" dropdown menu to edit a user's profile.
    
3.  **Invite a user.** Click "New user" from the window and input the users' information. Click "Save" and an invite email will send with instructions on how to complete their registration.
    

### Sign-in Activity

Tines retains 30 days 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.

Visit the Teams [page](https://intercom.help/tines-9c531f867f99/en/articles/6883947-using-teams-in-tines) for more information on team user roles.

## Use the event transform action

## Overview

The event transform action (also referred to as ETA) offers great versatility, with its primary purpose being to "transform" your event data in a highly flexible manner.

## Tines references

Before getting started, we recommend familiarizing yourself with the surrounding functionality this article covers:

-   [Tines - Event Transform Action](https://www.tines.com/docs/actions/types/event-transformation/)
    
-   [Tines - Events](https://www.tines.com/docs/events/)
    

## Make it happen

### ✅ Check one: What do you need the event transform action to accomplish?

With its multiple modes, the event transform action can cover a variety of use cases:

| **Mode** | **What it does** | **When to use it** |
| --- | --- | --- |
| [Automatic](https://www.tines.com/docs/actions/types/event-transformation/automatic/) | Leverages AI for data transformation | You want to utilize Tines AI to perform data calculations in one action. See also: [tips for automatic mode](https://explained.tines.com/en/articles/9369122-tips-for-automatic-mode). |
| [Deduplicate](https://www.tines.com/docs/actions/types/event-transformation/deduplicate/) | Only allow unique values to flow through based on a provided identifier. | Your user attempts to open multiple, duplicate tickets to an IT team, and you need to remove the duplicates so that only one is actually created. |
| [Delay](https://www.tines.com/docs/actions/types/event-transformation/delay/) | Create buffer time before the next action. | You submit a request via an HTTP request action to a service, but need to wait a few minutes before the result is ready. |
| [Explode](https://www.tines.com/docs/actions/types/event-transformation/explode/) | Execute every item in an array as a separate element and its own event. | You have an HTTP request and need to give it individual items and not a whole array. |
| [Extract](https://www.tines.com/docs/actions/types/event-transformation/extract/) | REGEX to extract data | You want to use a REGEX expression to extract specific values from a string. |
| [Implode](https://www.tines.com/docs/actions/types/event-transformation/implode/) | Assemble an array from multiple events that occurred previously in the story. | You have event data that you want to pull together into one event, either from an explode or from a [story fork](https://explained.tines.com/en/articles/9823926-example-approach-story-forks). |
| [Message only](https://www.tines.com/docs/actions/types/event-transformation/message-only/) | Leverages dynamic/static event data to emit | You want to restructure your data before the next action. |
| [Throttle](https://www.tines.com/docs/actions/types/event-transformation/throttle/) | Allow event data to emit at a rate that you determine. | You are reaching out to an API service that has a rate limit. |

### ✅ Check two: Familiarize yourself with each mode's configuration and options

A large aspect of Tines is configuring your actions to run the way you want. Let's take a look at some important tactile concepts.

When you pull an event transform action onto your storyboard, it defaults to Message only mode. You'll see that there are key components of the action, such as it's name and description, in the config panel on the right. As you use different modes of the event transform action, you'll see these change.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1306538459/3d5e970961e288c7c1c6734b2480/image.png?expires=1784786400&signature=1a4a3ba6c8e0ee48d8d7a9ae1591cec4482056ade2c13cd784b8fbe3b6d84cfc&req=dSMnEMx9lYVaUPMW3nq%2BgZxZ3ERjZ6vxFh%2FhK7jj86Bzj5dKlPYOhj0rV3IZ%0AGuiRRHAsuQJQO9BvjQDEQ2gD8Hc%3D%0A)

> **Tines Tip:** Built out your action, but need help naming it? Use our AI "Generate name" feature next to the action name ( ✨ icon) to automatically generate an action name based on what it's doing!

When creating an action, you'll find a **\+ Option** button at the bottom of the config panel. Some settings here can [apply to all modes](https://www.tines.com/docs/actions/configuration/#common-options), while others are [specific to the selected mode](https://www.tines.com/docs/actions/types/event-transformation/). Exploring these options over time can help you customize actions more effectively.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1306545292/0edd0184c2b3f62c90c3cc147b05/image.png?expires=1784786400&signature=4dc68c2feb2eff910bb927ca145cd266c408fcee70c588302b505e5059d8d85d&req=dSMnEMx6mINWW%2FMW3nq%2BgQD0LaT2EShXaGh74dXzORnBkR0YhOVx2%2FmTN2mQ%0AnL33mevzKfVyva63E8yQDoZ0Vu0%3D%0A)

## Using Dashboards

Dashboards are designed to help you monitor and report on your data. This article will help you set up a dashboard in Tines.

Read our introduction to dashboards [here](https://explained.tines.com/en/articles/9521649-introduction-to-dashboards).

## Create a dashboard

To create a new dashboard, click on the ‘+ New’ button. Give the dashboard a name and click ‘Save’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203614303/8b06d1a534f910f8819df7da72c4/Dashboards+-+Create+New.gif?expires=1784851200&signature=d1cae06722e4a8df0ddd58febbcb31e8ebe194842b43b4fc17a65f8f33194903&req=diInFc9%2FmYJfWvMW3nq%2BgRf4OfDKQXZDNEJuAuXtwynFDLjxS%2FS0cYIOGCkm%0ANCOAjUpCfIQrQmtPEdi%2Bso5Tkos%3D%0A)

### Teams and groups

Dashboards can be created within a team or a case group. You must select the team or group to view their dashboards. Dashboards are not inherited from a group up to the team.

> Note: The data available to a dashboard is scoped to the group or team it was created in.

## Duplicating dashboards

Dashboards can be duplicated within a team or group. You can also duplicate a dashboard from a team into one of its groups.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203479707/6606a38b4f6db7255a5e0f90ad32/duplicate+dashboard.gif?expires=1784851200&signature=6de6bd4e31fa3a59d05cdbc4034e91d63010f6e099cd98b63386f8a355b74a6f&req=diInFc15lIZfXvMW3nq%2BgXAcRw9qzjR1suR73iR0LqJ7AwOLl4Am8wmIvnHg%0AE2P1w5kN%2BBHdGL0LtmCUiR%2BBoDA%3D%0A)

> Note: The name must be unique and can be edited at any time.

## Adding components to your dashboard

There are three main components for building out a dashboard:

-   **Cases visualization:** Create a chart, graph, list, or number block representing case data.
    
-   **Records visualization:** Create a chart, graph, table, or number block representing records data.
    
-   **Notes:** context to provide alongside the charts or lists with markdown support
    

### Adding a cases chart

Use this option to build a filtered view of cases data. Cases charts are can be created within dashboards residing within a team or a case group.

Follow these steps to add a cases chart to your dashboard:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203615025/8645134e240063b274dc11b53b75/Dashboards+-+Add+Chart.gif?expires=1784851200&signature=1cda02dab0899579f4175099d392b16dc4f63fa11c028fbc388b0410f11251f3&req=diInFc9%2FmIFdXPMW3nq%2Bgbz4PlEr4y9LmT1j8TIVBPIN6EMXFrA0fP03BU47%0AeNh6XxkHQ1CPZ%2B%2Bpfi%2BtlQrM80U%3D%0A)

-   Click on ‘Add Chart’ or click the + icon and select visualization (if you already have graphs in your dashboard)
    
-   Select the “cases” toggle at the top
    
-   Give your chart a name
    
-   Select your:
    
    -   Data source
        
    -   Chart type
        
    -   Style
        
-   Set the maximum results you want to return (if applicable)
    
-   Add filters to narrow in on specific data (optional)
    

> Note: depending on your choice of data source, you will have the option of the following chart types:
>
> -   Line graph
>     
> -   Number block
>     
> -   Pie chart
>     
> -   List
>     
> -   Bar chart

### Adding a record

Use this option to build for a view of records data. Records charts can be created within a dashboard residing only within a team.

Follow these steps to add a records chart to your dashboard:

-   Click on ‘Add Chart’ or click the + icon and select visualization (if you already have graphs in your dashboard)
    
-   Click on the ‘Records’ tab
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203621313/741e5459b5df88b5119053480344/Dashboards+-+switch+to+records.gif?expires=1784851200&signature=4efa22a76967b8e5f05b405ad5439be7bea070f3c3f384948105b2ff91f7591f&req=diInFc98nIJeWvMW3nq%2BgX9cAzvgVCaWvVs2dgDLeKI0FFbvkSFLK%2BTJzKSZ%0AFb5ZlbmHWExKkVBM%2BKbDALzkZ84%3D%0A)

-   Give your chart a name
    
-   Select your:
    
    -   Data source
        
    -   Chart type
        
    -   Summarize
        
    -   Time Frame
        
    -   Style (if applicable)
        
    -   Maximum results you want to return (if applicable)
        

### Adding a note

Notes let you add explanations and context to your dashboard.

In the toolbar, click the + icon and select ‘Note’.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2203616337/cea9e34f878142165d9a5341a505/d0eeba67-a3dd-417a-b198-dfb41d82e035?expires=1784851200&signature=8d445af593019c8d1d93427ff34f07de4a01fdfb68079f443b36a1cff17ec3ea&req=diInFc9%2Fm4JcXvMW3nq%2BgY6W4JNZOGelEt%2Br%2Fy2gzbSat0pZkHDNa9ecyYuK%0Ax9dCXDwP%2BvP%2BUeoVHzgCTpAqTx0%3D%0A)

Once added, you can add the following to your note:

-   Text
    
-   Links
    
-   Checklists
    
-   Images
    

## Drill down

Drill down functionality is available in both your charts and record views. This is useful because it allows you to view the specific, related cases that make up a particular section of a chart or record view.

-   Float over a section of your graph
    
-   Hold command and click on a section of your graph
    

> Note: Hold ‘Shift’ to select more than one section

-   The related cases from that section will open in a new tab
    

Learn more about building dashboards in our [docs](https://www.tines.com/docs/records-cases/dashboards).

## Using explode and implode

This article and video explains how to use the explode and implode modes within the event transform action in Tines to process, clean, and reassemble array data. This is useful for manipulating data elements, such as removing unwanted characters, that would otherwise be difficult to do individually.

[Watch video](https://www.youtube.com/embed/nQivWrTMprM?rel=0)

> **Tines Tip:** For more background information on Implode and Explode actions, read here:
>
> -   [Explode mode](https://www.tines.com/docs/actions/types/event-transformation/explode)
>     
> -   [Implode mode](https://www.tines.com/docs/actions/types/event-transformation/implode)

## Steps

### Initial setup (the kickoff action)

The process begins with an action (referred to here as the kickoff action) that contains an array of data. In this example, the data includes email, name, and id fields for several people. Each field contains an unwanted extra character, a dollar sign ($), which needs to be removed.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2249230677/e8d3e17ff42f22b95a9958e7dc77/7b29f9d5-db2d-4734-89ab-3c26293930de?expires=1784786400&signature=36587379e292df4b09a449f5d46fe2914a8cda00085553801d6f7bd89aeecbb4&req=diIjH8t9nYdYXvMW3nq%2BgVHXgw96eID%2BZC5%2BlnwJdImYSqXAS70mYX9FwX2g%0A5eP9Znr%2FtvlTefZwJzVSEK4S6hc%3D%0A)

Here is the data if you want to follow along:

```
[
  {
    "email": "john$$doe@example.com",
    "name": "John$$ Doe",
    "id": "133445"
  },
  {
    "email": "jane$$doe@example.com",
    "name": "Jane$$ Doe",
    "id": "198876"
  },
  {
    "email": "jessie$$doe@example.com",
    "name": "Jessie$$ Doe",
    "id": "192837"
  }
]
```

### Step 1: Explode the data

To process the data points individually, pull in an event transform action and connect it from the kickoff action. This is the explode step.

1.  Name the action (e.g., Explode).
    
2.  Set the mode to "Explode".
    
3.  Set the path to reference the source data array (e.g., `kickoff`).
    
    -   By default, the exploded items are named `individual_item`, but you can rename this if desired.
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/2249233662/925de1e379553c3ba4ddde89100e/Explode.gif?expires=1784786400&signature=41ae48a22f4d65379577dcccbe83c21f9172ad41d0c3c3c8a7bae63db4c7da40&req=diIjH8t9nodZW%2FMW3nq%2BgSO4ZtjWhfwbm2xI8Mm7fqfteql7OaH8tae8Uz%2BG%0ANtHc3hOGWuyD72%2FMnfXvdYC7cWY%3D%0A)

Running the workflow will cause the Explode action to generate multiple events, one for each item in the original array. The unwanted characters are still present at this stage.

### Step 2: Format and clean the data

Next, pull in a second event transform action and connect it from the explode action to clean and format the data.

1.  Name the action (e.g., Format Data).
    
2.  In the message field, set up the desired key-value pairs (`email`, `name`, and `id`) to maintain the original data structure.
    
3.  For the value of each key, use a formula to reference the exploded item's data and remove the extra character. The `REMOVE` formula is often the simplest method for this.
    
    -   Example formula for email: `REMOVE(explode.item.email, '$')`
        
    -   Apply this same formula logic to the name field, adjusting the data pill reference (e.g., `REMOVE(explode.item.name, '$')`.
        

![](https://downloads.intercomcdn.com/i/o/am0g137l/2249240712/e47830ae952def5aa83dcb0599f8/f5746ebd-d1cc-4a5d-8f78-375f9ada9206?expires=1784786400&signature=311ba6d8abc48f17e4c5aac4e16604273e39a00dfbb78c7667a739f7f641646d&req=diIjH8t6nYZeW%2FMW3nq%2BgRiZX4SxuEOTI6QSIGR9%2BEQUkRnu4i6cpk7t8Oao%0AwHSgprBbDmjZfe%2Fw5HEzK%2BVNbjE%3D%0A)

Running the workflow now shows that the Format Data action has cleaned the email, name, and ip fields by removing the extra dollar signs, modifying the data to the desired format.

### Step 3: Implode the data

Finally, use a third event transform action in implode mode to combine the individual, cleaned events back into a single list.

1.  Name the action (e.g., Implode).
    
2.  Set the mode to Implode.
    
3.  Set the item path to the action containing the cleaned data (e.g., Format Data).
    
4.  Set the guide path to `explode.guid`. This is the unique identifier created when the initial explode action happens.
    
5.  Set the size path to `explode.size`. This tells Tines how many items were exploded.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2249243778/f4b38696dcc88722c26f69053e8d/0a69f83e-2140-45ed-8d23-46bad97ebc48?expires=1784786400&signature=457e0b8d27bcec3d9e880de57f76e3817a673c00cf3babd28e5ce9a3d8247f76&req=diIjH8t6noZYUfMW3nq%2BgYnM8pd07WUQ3%2FOB2%2FbSRxyG4Do8QE2c9%2BvvoFIk%0AAdYWhGrheZbzT%2Flm8b6oRRZtVLA%3D%0A)

When the workflow is run, the Implode action will output a single item containing the complete list of cleaned data, pulling everything back together. This showcases how you can quickly use a couple of different event transforms to achieve cool results.

## Event processing flow of explode actions

When a story is started, a story run GUID is created. This is a unique identifier for that story run. For each action that is executed, they have an event ID, which is unique to the singular execution of that action.

An explode action treats however many events were emitted from it *almost* as different story runs. So, they're emitted and they can flow through the rest of the story in parallel to each other, but they maintain the same story run GUID.

We do not respect the order of the events from an explode. For example, when these events are submitted, if you run the same transformation against 50 events and then use an implode action, the implode can and will receive them out of order. This is because, on our back-end, some processors will do the same job slightly faster than others, and while we process as first in/first out, we can't guarantee that an implode will be in the same order as it was sent out.

## Example: Exploding an array

This video explains how to explode an array in Tines using a get cat picture example.

[Watch video](https://www.youtube.com/embed/W9geiPgPuiI?rel=0)

> **👉 For a more detailed description of event transformation** [see here](https://www.tines.com/docs/actions/types/event-transformation/)

## Using functions

## How to use the FILTER function

The below video outlines how to use the [FILTER](https://www.tines.com/docs/formulas/functions/filter) function in Tines.

[Watch video](https://www.youtube.com/embed/4Z82ifWvBNk?rel=0)

> **👉 For more details on functions in Tines, [see here](https://www.tines.com/docs/formulas/functions).**

## How to use the map function in Tines

This video explains how to use the map function in the event transform in Tines to filter to a portion of an array using an identified key.

[Watch video](https://www.youtube.com/embed/w6gdGboCp_Y?rel=0)

> **👉 For a more detailed description of map including examples** [see here.](https://www.tines.com/docs/formulas/functions/map)

## How to use the push function in Tines

The below video outlines how to use the [push](https://www.tines.com/docs/formulas/functions/push) function in Tines.

[Watch video](https://www.youtube.com/embed/qqxIy_DCmhI?rel=0)

## Using groups

Is your Tines story starting to look a little long and complicated? Groups can help you turn sprawling workflows into clean, organized, and easy-to-manage automated processes.

## What is a group?

A group is simply a collection of actions within your story. It lets you bundle a series of related actions into one place, making your story more organized. By using groups, you make your complex stories much easier for anyone on your team to understand, troubleshoot, and maintain.

It's the perfect way to take a distinct part of your workflow—like checking an IP address, formatting a security alert, or creating a new ticket—and tuck it neatly away under a single, identifiable action.

## How to create a group

In your storyboard, select the actions you want to combine. Then, choose Group, or use the keyboard shortcut Cmd/Ctrl + G.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2095372321/f1d59b4873e047147a992b904d41/Create+a+group+of+actions.gif?expires=1784851200&signature=9669d00690f9d68802a7a67aae2915212e51923c0c6f846e0f5078b99828be87&req=diAuE8p5n4JdWPMW3nq%2BgbsKtL%2Fbobuz0mtflNPMrIp5ZG8RfepJpaKV3N3K%0AodXfS8N6CJE0TnFqOeWV55%2BsncY%3D%0A)

## Functionality

### Passing data

Groups make it easy to control the information going in and out. You can define specific inputs to send data into your group and configure the output to return exactly the data you need back to the main story.

### Handling lists with loops

If you have a list of items (like a list of 100 email addresses, or 50 security alerts) that all need the exact same process applied, a group can do it. You simply tell the group which list to process, and it will automatically run all of its internal actions for every single item.

This ability to automatically process lists makes your stories highly scalable.

## Setting inputs and outputs for groups

Group inputs and outputs are used to create a clean interface for data to flow into and out of a group, making groups work like reusable components or functions. Every group you create will have both a group input and a group output, allowing you to manage the data exchange between the group and its parent story.

### Group input

The group input allows you to define custom fields that can pass data into your group from the rest of your story. They act as the parameters for your group. This makes it easy to send necessary information to your group to execute its defined actions.

A group input can be configured with one or more custom options, each supporting a specific data type:

-   **Object**: Displays an object builder for structured data input.
    
-   **Plain Text**: Renders a simple plaintext input field.
    
-   **Checkbox**: Renders a single-option checkbox.
    
-   **Number**: Renders a number input field.
    
-   **Formula**: Renders an input field that supports Tines formulas.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2277360742/45c7f5b2d84ab6a914fe0dd66f5d/17ea6279-a355-47db-a268-84e895cead2b?expires=1784851200&signature=1d9d4110f30a67d69821c14fddbcaee6b43d6333b5646807fa8178acf377deb5&req=diIgEcp4nYZbW%2FMW3nq%2BgdB0%2BaK1FIkCeC04pGbQ3rBUtKqoV%2B1keltLJMgM%0A6%2FANpzpO0qlfAGvUsPFIIVHSND0%3D%0A)

### Group output

The group output is where you configure the data that will be returned from the group back to the parent story. This ensures the results of the group's actions are correctly formatted and available for the next action in your workflow to reference the data.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2277360741/96255c26205fc83cc3ecdb145d30/91490476-7b7d-432c-975f-5b66394d201f?expires=1784851200&signature=2fe02b46eb2139d4a8f1a3983c57e5ba1606e8077614512c4b5e95e9f0e2d1b0&req=diIgEcp4nYZbWPMW3nq%2BgfeC%2B9EE8ZiSZlpA07W6k1nMVy8s0NIk4APvnobZ%0Aa761Xf4yeiiHYIsQFJWh8oNrB0Q%3D%0A)

> Note: A group of actions can have only *one* output.

You can configure a group using triggers so that only one of the available outputs will be outputted from the group. For example:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2277448235/00635050c3cda4fb51a80ca2f38d/Action%2BGroup%2Bwith%2BTriggers.png?expires=1784851200&signature=eb52f09a99833667f38de18820ba53168b23f46b18846096de035bb711f94b25&req=diIgEc16lYNcXPMW3nq%2BgQo4dPwdZ%2FpOcKtDHc2rTDI7BJMjAt6Oxln4D5J2%0A3u3CA7ajvJ89G4HeueiuoFeh%2BuU%3D%0A)

### Example use case

Imagine a group called "Enrich IP Address":

-   **Group input:** Receives ip\_address from upstream actions
    
-   **Internal sections**: Looks up geolocation, threat intel, WHOIS data
    
-   **Group output:** Returns enriched data object with all findings
    

The result is that any action can send an IP to this group and get back enriched data, without knowing how enrichment works internally.

Without group inputs and outputs, actions inside a group couldn't receive data from outside, and actions outside couldn't access the group's results.

## Using the loop option in groups

When building stories in Tines, you may need to perform a sequence of actions on every item in a list - for example; a list of users, security alerts, or IP addresses. Instead of manually creating separate workflow branches for each item, you can use the loop option in groups. Using the loop option when configuring your groups allows you to apply your logic to every element in an array automatically.

### How it works

When you configure a group with the `LOOP` option, Tines will:

1.  Take an array (a list of items) from a preceding event.
    
2.  Invoke the entire group for each element in that array.
    
3.  Emit a single output event containing an aggregated list of the results after all loop iterations are complete.
    

### Loop object

When configuring the loop, specify the path to the array you want to iterate on. Within the group, you can refer to the current item being processed using the special `LOOP` object:

-   `LOOP.value`: The current element from the array (e.g., the IP address).
    
-   `LOOP.index`: The position of the current element in the array (starting from 0).
    

### Serial vs. parallel processing

Groups offer two main modes for processing the loop:

-   **Serial**: The elements are processed one after another to preserve the original order of your array in the final output. This option is selected by default.
    
-   **Parallel**: All elements are processed simultaneously. Use this option for better performance when the order of the items in the list doesn’t matter.
    

> **Note:** The parallel processing option does not guarantee the order of the elements is maintained in the final output.

### Important considerations

#### Loop size limit

A single loop can run on an array containing up to 20,000 elements. For larger arrays, it is recommended to:

1.  Use the `CHUNK_ARRAY` formula to break the list into smaller sub-lists.
    
2.  Use an explode mode event transformation action to emit an event for each of the sub-lists.
    
3.  Loop over the lists contained in each emitted event
    

#### Error handling

If any single element in the array causes a failure within the group, the entire loop processing is stopped. You must build in error path handling (with a dedicated outgoing event from the group) to ensure your Story can continue processing if an element fails.

## Group restrictions

There are a few actions you cannot currently place inside groups. Specifically, you cannot group the following action types:

-   Pages
    
-   Webhooks
    
-   Receive Email
    

Additionally, the actions within a group cannot be set up to run on a schedule.

> Read more about groups in our docs [here](https://www.tines.com/docs/stories/groups/).

## Using loop in Tines

Looping is a common method to iterate through a list in Tines.

## Enabling loop

Drag an event transform action onto your storyboard. On the configuration panel on the right, click on "Loop".

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244780598/4dac61379932ddd8c7af2aa558bf/enable+loop.gif?expires=1784851200&signature=a5342f5e6660180ad0c2a598f729485a68585ba258a61da3c8d4f4c95089af2b&req=diIjEs52nYRWUfMW3nq%2BgYiOXSi4bHD6WqrNphYxSLKnrtZ%2BpnaHotQ3MhcL%0AnohUbBC0sBaTOc31nAMWVzCvXkY%3D%0A)

> **Note:** The local value will reset at the beginning of each loop iteration.

## How to use use loop

You can follow along in the following video:

[Watch video](https://www.youtube.com/embed/w_amfsa0ido?rel=0)

Or, look more closely at the examples by going to the story library and importing the [explore loop example](https://www.tines.com/library/stories/1160559/?name=explore-loop-examples) story into your tenant.

### Use the explode event transform action

The first and easiest way to loop through a set of arrays in Tines is to use the explode event transform action.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244660763/1aa5e4746e84dfbcaec8e2a1ddb2/848294a6-e507-46c4-93b9-f58123686310?expires=1784851200&signature=9d84915198712cc979faa9a0b8cd428e9ee5eeb06909720183b18d710d6a92e2&req=diIjEs94nYZZWvMW3nq%2BgW5J%2Bx%2FSx18ZvvMbTO%2F0hh%2FuNNb5sK5ylCID2DCd%0AnHnQIuYq1K3ePR24tGFKFnef2eg%3D%0A)

Explode allows you to loop through each array that is specified in the path and output multiple events related to each specific array that exists.

This is useful when you want to perform enrichment or further actions on each specific array in a new unique event.

### Use the event transform action in message mode

The second way to loop through events is with the event transform action in message mode with the loop option enabled.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244660765/37f3a9f03b599746278ace921004/2d54f1e9-9af4-4a0a-8f9c-4979e97302a2?expires=1784851200&signature=f1b7ceee87b23d5bc1e9f6ef0c32b07e34a57f4feff7ac47685f01b6d3b20990&req=diIjEs94nYZZXPMW3nq%2BgQSj8nhR16AxdQcStOd3ZLHu7sLNT2cZxrJATzzR%0ACKR8bCToRd20xZi6XEJ26%2BBmYRk%3D%0A)

The main difference with this approach is that a single event is emitted instead of multiple. This allows you to create a new single event using array data that has been looped through.

### Use tags

The third way to perform looping is to use tags.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2244660764/3bab33522cd37799e2c3b0f0efff/69d0728b-e3fd-4965-abc9-040122bb36f8?expires=1784851200&signature=c039cc5d8bf0804eb050a570ea4ebe081fc8fbfc0b17b7897461d1c954f802d8&req=diIjEs94nYZZXfMW3nq%2BgbY6K%2FbADUfIWg4EJRV3zlZpQnS7cub0JJLijh%2Bk%0AivTpUp%2F%2FiKZUEvBGzeFthan2JVY%3D%0A)

This method comes in handy if you have two different groups of arrays and you want to loop through them.

> Note: If you are familiar with Python, this will look similar to how a for loop is constructed.

## Example: How to loop through data with event transform action

This video outlines how to use event transform to loop through an array of data in Tines.

[Watch video](https://www.youtube.com/embed/J4KXf7C7LzE?rel=0)

> **👉 For examples of using loop** [see here](https://www.tines.com/story-library/1160559/explore-loop-examples)

## Using Pages in Tines

Pages in Tines are web pages connected to your stories that your end users can provide input to (and view output from) your workflows. This article will go through how to use pages in Tines.

For an introduction to pages in Tines, read [this article](https://What%20are%20pages%20in%20Tines?).

## Collecting inputs with forms

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](https://www.tines.com/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.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218788609/ce50f63ff037ab681d672f8c57db/pages+fields.gif?expires=1784851200&signature=84e54565146e4ef8cf709137f5f3a0aa1205aa99f82fe7ba0eee23ce7f6eb393&req=diImHs52lYdfUPMW3nq%2BgaJ%2BO8OQxm5tMo%2Fhz0FLT%2B7JXzjoTmQE%2FMu7lQ5J%0AZcDXGWYbbhfG3ZKESoHkP4qgzlg%3D%0A)

When adding a field to a page, you can select from the following options:

-   Short text
    
-   Long text
    
-   Email
    
-   Web URL
    
-   Option
    
-   Date or time
    
-   Boolean
    
-   Number
    
-   Slider
    
-   File upload
    
-   Password
    

When the field is added, you can configure it with the following options:

-   Helper text
    
-   Multi-select
    
-   Required
    
-   Default value
    
-   Custom validation
    

## 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](https://intercom.help/tines-9c531f867f99/en/articles/6877834-events-in-tines) 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://downloads.intercomcdn.com/i/o/am0g137l/2218789740/ace23fb2c385a8ff54a3127ea068/944ddaa4-fa6d-4af5-bd8c-c46f0e520726?expires=1784851200&signature=d82663690eb9744a1af829af70a16940ae3d247bc10fca93b51f1064ebf085a2&req=diImHs52lIZbWfMW3nq%2BgcQvbi1zaiRTg0ztowZ%2F%2BHxpig2J%2BEwPx7YnAHZ7%0Anf1iAGzknBHjRPVbGhh%2BgXIVvv8%3D%0A)

The "Send an email" action could then access the submitted page data using a [formulas expression](https://intercom.help/tines-9c531f867f99/en/articles/6878126-referencing-data-with-formulas) like the following:

`new_page.body.example_input`

## How can I directly access a page in a story?

A page can only be directly accessed if it is the first action in a story without any input event into the page action.

If a page has an input, the page will only be created and a link generated when an event comes into the page as the information submitted in the page will continue the same story run as shown below.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2218790706/92dde5c36df64b8fa32c1a652ffc/eb09438d-3d98-4407-a989-e30478314fb6?expires=1784851200&signature=4d8a02ec44f24f6a30e02da6f3db13e960c1bf374364ab37d559628a0ed00471&req=diImHs53nYZfX%2FMW3nq%2BgQbEUFlM59ODQOLGhWkrKJQdnD%2FaD3SiR23iZeQ3%0AxDhY6H6fqRzXPHQrHgliTKyKHZI%3D%0A)

> Read more about pages in our docs [here](https://www.tines.com/docs/pages/).

## Using Teams in Tines

When invited to a team, you can then segment users and objects. Objects in Tines include credentials, resources, and workflows (i.e., Stories).

### **How does it work?**

Users can only access the objects within their designated teams. Tenant owners are the only ones who can view all teams and objects across the tenant.

### **How do I invite users to a team?**

Adding members to a team is simple:

1.  Open Tines and click on your team name
    
2.  Click `Settings` and then `Users`
    
3.  Hit `+New` and enter the email address of the user you want to invite. You can separate multiple email addresses with a comma.
    
4.  Optional: Assign teams and roles
    

After a team member accepts the invitation, you can configure their role.

### Understanding Roles

Roles in teams include admin, editor, or viewer. Each role within a team is designated certain permissions and levels of access.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2035973438/373dd8b7104a3a380ab59a45ae1f/CleanShot+2026-02-06+at+15_24_29%402x.png?expires=1784851200&signature=d937085c7d8aa1d4f998c2860c0d6fa69889d4f5b56d06ec241cca975df434a8&req=diAkE8B5noVcUfMW3nq%2BgVgahZEJJnJXq%2FM5RxDOSAFwvlgIx%2FrCW%2FR2wAf8%0AKout8YrkreKrnk1SmlER3KJQ96E%3D%0A)

> 👉 **Learn more about roles in Tines [here](https://explained.tines.com/en/articles/13367762-roles-in-tines).**

#### Team Admin

This is the editor who also manages the team. They have unrestricted read and write permissions for all objects within the team including:

-   Add, manage, or remove members of a team
    
-   Perform destructive actions such as deleting an object
    
-   Push changes to a workflow live through [change control](https://www.tines.com/docs/stories/change-control)
    

#### Editor

This editor has read and write permissions to *most* objects across a team, but they cannot manage member access to the team or perform destructive actions.

This role is useful for most users who simply need the ability to create, edit, and run Stories in Tines.

#### Viewer

This role gives a user **read-only permission** across a team. They can view, but not edit, objects in a team.

This role is 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.

#### Permissions table

This table provides a detailed view of the exact permissions for the different roles.

| **Permission/Team Role** | **Team Admin** | **Editor** | **Viewer** |
| --- | --- | --- | --- |
| **Read objects** | **x** | **x** | **x** |
| **Interact with pages** | **x** | **x** | **x** |
| **Create new objects** | **x** | **x** |  |
| **Edit existing objects** | **x** | **x** |  |
| **Run an action** | **x** | **x** |  |
| **Import a story** | **x** | **x** |  |
| **Export a story** | **x** | **x** | **x** |
| **Push changes to a story** | **x** |  |  |
| **Manage team members** | **x** |  |  |
| **Share objects** | **x** |  |  |
| **Move objects** | **x** |  |  |
| **Delete objects** | **x** |  |  |

> *Objects are events, resources, credentials, records, or workflows (Stories) in Tines.*

### Object sharing across teams

By default, all objects are shared to the team where it was created. This enables editors to reuse a single object in multiple places. As a result, when an object is changed or updated, that change is reflected everywhere the object is used.

##### Limiting or increasing access to an object

When creating a credential or resource, the editor defines the access level. They can choose to share it with all teams or select specific teams. This is useful when some teams shouldn't be able to access a credential or resource.

> **Note:** all objects shared outside a team **must have a unique name**.

More information on Story sharing configuration can be found with the [Send to Story](https://www.tines.com/docs/stories/send-to-story) configuration options.

## Team story allocation

If you have multiple teams in a tenant, the tenant owner can provision story limits by team.

1.  Go to your main menu in the top left corner
    
2.  Click `Settings` and then `story allocation`
    
3.  Check the box for the team(s) you want to limit
    
4.  Set the maximum stories for the team(s)
    
5.  Click `Save`
    

You can always come back and modify these settings.

* * *

*Please note: depending on your plan, you may only have access to a certain number of teams and builders (editors and team admins). If you need to increase your limits, please reach out to your Customer Success Manager. [See the Pricing Explained for more information](https://explained.tines.com/en/articles/7007597-understanding-tines-pricing-plans).*

## Using the send email action

## Configure emails

-   Start by dragging a send email action onto the storyboard.
    
-   Select the send email action to configure.
    
-   Configure the email in the properties panel on the right.
    
-   You can configure the reply to address, sender name, subject, and the body of the email.
    

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273052274/6346f0d0abae76f648f765d02bc1/send+email+configure.gif?expires=1784786400&signature=be04c20f79fda5c8cc7eff52eb1f3bc26d0683ba52045d357fd0352c9ced8e2a&req=diIgFcl7n4NYXfMW3nq%2BgTmmcpDwQWhQpd2JrmAH0xRugJaEMKs6xgaNz0Zx%0AB94uFAXWQ9zO3qZZWh392gqkAmg%3D%0A)

There is an HTML editor available in Tines to format the email body.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2273053362/bfc675cd45d831eaa083b6502f36/send+email+html.gif?expires=1784786400&signature=48afd67115f3628b6974d7904c3b0c337cf0e18c902ada50e057b266cecae77f&req=diIgFcl7noJZW%2FMW3nq%2Bgccz971buqOOwm8maKB%2BJycHTj4U99AfAU%2FKu40c%0Ah20J7fO3sdfTKX81WKfxiYtZTsU%3D%0A)

### Email attachment size limit

There is currently no limit to the size of email attachments in Tines.

## Sender detail tips

### Trusted senders list

To ensure that you receive all emails from Tines, add the following addresses to your trusted sender's list:

-   [support@tines.com](mailto:support@tines.com)
    
-   [support@tines.io](mailto:support@tines.io)
    
-   [mail@tines.com](mailto:mail@tines.com)
    
-   [mail@tines.io](mailto:mail@tines.io)
    
-   Any custom email senders configured on the tenant
    

If you continue to encounter issues, please contact [support](https://www.tines.com/contact) as we may need to remove your email address from an Amazon blocklist.

### Mail exchange (MX) record

The MX record value is necessary for the custom sender email address feature.

Using the [custom sender email address](https://www.tines.com/docs/admin/custom-sender-email-addresses) feature, you will need to create a subdomain (i.e. `security.tines.com`) in order to get (i.e. `tines.com`) to work as a sender domain. The subdomain is used to verify that you own your domain, and does not actually have a mail server.

Tines' custom sender email address feature is powered by Amazon SES and MAIL FROM, and the need for the MX record is a requirement from AWS: [Using a custom MAIL FROM domain](https://docs.aws.amazon.com/ses/latest/dg/mail-from.html#:~:text=SES%20requires%20you%20to%20publish,send%20email%20from%20your%20domain).

> To learn more about the send email action, [see here](https://www.tines.com/docs/actions/types/email).

## Using the Tines Tunnel

The Tines tunnel provides a way to securely access your systems running on private networks from the Tines cloud environment. It is deployed as a container service.

Get started by opening your tenant and appending the URL with: `/admin/tunnel`. This gives you the relevant information to deploy the tunnel container.  
​

## Deploying the tunnel

The tunnel container can be run with Docker using the example below or similarly with other container orchestration platforms.

```
docker run \
  --name tines-tunnel \
  --env TINES_TUNNEL_SECRET="secret" \
  tines/tines-tunnel:latest
```

The tunnel service uses the host routing and DNS services where it is deployed.

##   
Using the tunnel

HTTP Requests can be sent through the tunnel by selecting the `Use tunnel` parameter in an HTTP Request action properties panel.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1750723875/f78d85e9230efade115ea9ea664d/HTTP+action.png?expires=1784786400&signature=bbee558cb8a233b7e0e0898dfb8c68166796fb94f0a7717db06f42f878cee700&req=dSciFs58nolYXPMW3nq%2BgXcjBzs0lYr0SgozmcZ%2FxY3dpn4aVD4DWk%2FLX8PV%0APfaTPcPKF0cSyA%2Fe%2B%2BHjpnRmHi4%3D%0A)

![](https://downloads.intercomcdn.com/i/o/am0g137l/1750716254/ec37fb915faccfd5784d14754074/Tunnel.png?expires=1784786400&signature=fc816e1f449cf04d509e1d5e897663c657918a587068cde393689468e3b578f7&req=dSciFs5%2Fm4NaXfMW3nq%2BgT5ecSjAqjygv56nvbjpohRo52985OpQCHphqYQG%0AKs3H11UTT1luP3%2BukTmIMBERmGk%3D%0A)

## Connectivity Requirements

The Tines tunnel uses [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps). To function properly, it requires connectivity to Cloudflare. It does not require additional inbound from Tines, the internet, or elsewhere.

The container will attempt to form a connection with the services below from its deployment location.

| **DESTINATION** | **PORT** | **PROTOCOLS** | **DIRECTION** |
| --- | --- | --- | --- |
| region1.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux | Outbound |
| region2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux | Outbound |
| region1.v2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux | Outbound |
| region2.v2.argotunnel.com | 7844 | TCP/UDP/QUIC/h2mux | Outbound |
| api.cloudflare.com | 443 | TCP/HTTPS | Outbound |
| updates.cloudflare.com | 443 | TCP/HTTPS | Outbound |

If utilizing strict TLS/SSL inspection, exclude the above HTTPS traffic from the interception policy and outbound traffic to `cftunnel.com`.

For more information, see Cloudflare's documentation [here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/do-more-with-tunnels/ports-and-ips/).

> ➡️ The tunnel is an available add-on for cloud customers.  
> Learn more by contacting [Tines support](https://www.tines.com/contact-support).

## Using Workbench

### Enabling stories for Workbench

As explained in an [Introduction to Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench), the product is user-scoped. Any builder within a team can enable a story for Workbench via the [send-to-story](https://www.tines.com/docs/actions/types/send-to-story/) settings in the build pane. There are two options for stories and Workbench:

-   **Workbench**: this means the story is only available to use in Workbench, not other stories. These stories *do not* count towards your tenant limits as they cannot run autonomously.
    
-   **Workbench and send-to-story**: this means it can be referenced in either Workbench or another Story. This is great if you’re looking to leverage existing stories in Workbench as well. These do count towards your tenant limits.
    

![](https://downloads.intercomcdn.com/i/o/1175830622/b15082d866d3c232c9eda16c/Enable+Workbench+stories.gif?expires=1784786400&signature=ea2e5098410e6cc47029db5f45880147ea0b635e7df51667b4ea134f70eba22c&req=dSEgE8F9nYddW%2FMW3nq%2BgbnLmek34FLP6IQHGgEy6I0wCdRv4RCT6HCHkl36%0AspBtZVrRSjM1LDQQzjD9%2BzEtysA%3D%0A)

In this video, we'll learn how we can add a story to be accessible by Workbench.

[Watch video](https://www.youtube.com/embed/Kj-e3CLjngM?rel=0)

### Finding optimal efficiency with Stories and Workbench

With Workbench, you are in the driver's seat. You determine what Workbench can and cannot do within your tenant based on the stories you enable.

For ideal functionality, make sure your send to story inputs are well-defined with descriptions. The stronger a story description, the better Workbench will understand your task and perform your request. This is especially helpful when you have similar stories available to Workbench.

### **How to chat with Workbench**

In the below video, we'll take a look at how we can interact with Workbench.

[Watch video](https://www.youtube.com/embed/lx2XeedEcCk?rel=0)

### **Leverage templates and stories in Workbench**

In this video, we'll take a look at how we can activate available templates and stories to our Workbench instance.

[Watch video](https://www.youtube.com/embed/9txpybCFWNo?rel=0)

#### **Looking for inspiration?**

Workbench is useful to any team! Here are a few ways different teams can take advantage of the product

-   **Security:** enrich user and asset information, investigate EDR alerts, analyze and block IPs, domains, and URLs, run real-time response and forensic investigations
    
-   **IT:** create a ticket in Jira, lock down a device in Jamf
    
-   **Any Tines builder:** view upcoming meetings, send a Slack message, create a new document in Google Docs
    

If you need inspiration, explore our [Library](https://www.tines.com/library/). We’ve added a few stories that you can import into your own tenant. Each story includes a complete look at the chat conversations to give you an idea of what the output may look like.

## Venafi Authentication Guide

Venafi provides global visibility of machine identities and their associated risks for the extended enterprise on-premises, mobile, virtual, cloud, and IoT. Venafi delivers innovative solutions for the world's most demanding, security-conscious Global 5000 organizations and government agencies.

## First, get a Venafi API key

1.  Login to your Venafi account
    
2.  Click on the user menu and click "Preferences"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929245556/6a9a743119615ad47cc98de3/image.png?expires=1784851200&signature=27d3033a401c0015bf882c4c2bd6528800902172610e05ec4323c820314d8921&req=fSIuFM17mIRZFb4V1XW4gW08aT8f8rHh8RpNUY1mfnEDdJ1GpncA0eTh%2BfgN%0A0R5jAYKqMs8Z6d%2Fxs5GkoKGrYg%3D%3D%0A)
    
      
    ​  
    ​
    
3.  Click on "API Keys" and copy your API Key to a text editor.  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/929246940/1aa6850f3ddeade3873ff5c3/image.png?expires=1784851200&signature=84f188ce0a449cf50de78b5dfd9e549ee5d49a4e46fd6ec17b1c16b54dcc8bb6&req=fSIuFM14lIVfFb4V1XW4geuaN7ZTjKTEhjKu%2FO20205WYXc%2BPlifVtf7nNQe%0AkhEQMYk2WnYUZFD0Uu8iZwbrpg%3D%3D%0A)
    
      
    ​
    

## Lastly, create a Venafi credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Venafi credential
    
    1.  **Name:** Venafi
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Venafi** **stories in the [story library](https://www.tines.com/library?s=venafi&view=all)**

## VirusTotal Authentication Guide

VirusTotal allows you to analyze suspicious files, domains, IPs, and URLs to detect malware and other breaches and automatically share them with the security community. Here's our guide for integrating with VirusTotal using Tines:

### First, get a VirusTotal API key

1.  Visit [www.virustotal.com](http://www.virustotal.com/) and sign in. You'll need to sign up if you are not an existing VirusTotal customer.
    
2.  Click on your profile in the top right-hand corner and select **API key**:  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/732525704/3a508e38a0df1bc814052b61/image.png?expires=1784851200&signature=f4cf9db4993107729cec77c23547c5e025257dfff93b0a23561133a823a3995b&req=cyMlE8t7moFbFb4V1XW4gcCJ1LvIbgwCWPI49aaxPgc7YKLNrFeSr%2BZS9qwW%0ATrEhPV%2BAVfProx2Mlek5WuTgHw%3D%3D%0A)
    
3.  Copy the API key.
    

### Then, create a VirusTotal credential in Tines

**VirusTotal connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "VirusTotal" and follow the prompts to connect.  
    ​
    

**Manual credential creation**

This method is not recommended as the VirusTotal connect flow creates this credential with much less complexity. However, if you need to create this credential set manually, see the following steps below:

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the VirusTotal credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        

> Learn more about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/virustotal).
>
> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).
>
> You can find a selection of VirusTotal stories in the Tines [story library.](https://www.tines.com/story-library?s=virustotal)

## WatchGuard Authentication Guide

**WatchGuard** provides network and endpoint security, secure-WIFI, multi-factor authentication, and network intelligence products and services.

## First, get your WatchGuard API Key

1.  Log in to your WatchGuard Cloud account
    
2.  Select "Administration" and then "Managed Access"
    
3.  Click "Enable API Access".  
      
    ​
    
    ![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/771308994/3e98384bc5966eb6ff51df74/api_access_disabled.png?expires=1784851200&signature=5196269c328c1beb56e81a93639fa2d4e78eecddfeebddbd8a4591abeeb3870c&req=cycmFcl2lIhbFb4V1XW4gf2ieRNYqUxByoy2mrFkMKVBqvDXv8IgOJ6obhYi%0AnqsqiWYH8g1wzX0lNd0NRZgvZQ%3D%3D%0A)
    
4.  Specify the "readwrite" and "readonly" passwords to use as your API access credentials, then check the check box and click "Save"  
      
    ​
    
    ![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/771309000/893d33c1ac7c63d3fed5db00/api_access_passwords.png?expires=1784851200&signature=038e281de995feac737b20f659f4b38e1cb10fe1ed57b5d9fd1fce25f0011b0d&req=cycmFcl3nYFfFb4V1XW4gTnkpCF0H0NlBg%2BTFpOVpyXPFb2BxGRvDLt4S%2BNv%0AWGjiaLYeUpTiHK10j7qx4Xq85Q%3D%3D%0A)
    
5.  Once enabled, you will see the following information. Take note of
    
    1.  "Access ID (Read-write)
        
    2.  "Authentication API URL"
        
    3.  "API Key"  
          
        ​
        
    
    ![Screen shot of RESTful API Access enabled](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/771309004/66d3b0e74de872e2af1c35e3/api_access_enabled.png?expires=1784851200&signature=d6798146597351d50ea8893457fe738ca3a8c28185c3f11f2514110ae7ea2960&req=cycmFcl3nYFbFb4V1XW4gTCQQXFRFicFLmsTrRpE9Wp5FzfKkFEth6hkYk3I%0ATt1abbwoSkXclHH%2BtUdHit6ApQ%3D%3D%0A)
    

## Lastly, Create WatchGuard credentials in Tines

### HTTP request type to obtain access token

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "HTTP Request"
    
4.  Input the values for the WatchGuard credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **URL:** The "Authentication API URL, i.e. `https://api.usa.cloud.watchguard.com/oauth/token`.
        
        1.  Depending on your WatchGuard Cloud account data storage region, this may different.
            
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** `{"grant_type": "client_credentials", "scope": "api-access"}`
        
    7.  **Headers:**
        
        1.  `{"accept": "application/json"}`
            
        2.  `{"Authorization": "Basic dXNlcm5hbWU6N3loVHI1RHNkLWg2YTktODVnZS1nNjc4OTNoNmUwdGQ"}`
            
            1.  **read-write access ID:read-write password** encoded into base64
                
    8.  Click "Run options" and double click on the key named `access_token` in the response.
        
    9.  **Location of token from response:** Paste the value copied in the previous step.
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

### Text type for API Key

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the WatchGuard credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value: "**API Key"
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        

> For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).

## Using the credential in an action

When you make an API request, include the HTTP request type credential in the `Authorization` header after "Bearer" like:  
​

```
Bearer <<CREDENTIAL.watchguard_token>>
```

and the text type credential in the `WatchGuard-API-Key` header like:  
​

```
<<CREDENTIAL.watchguard_api_key>>
```

​

  
An example action you can copy and paste onto your storyboard:

```
{"standardLibVersion":"29","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Get account information","description":"Created from cURL command","options":"{\"url\":\"https://api.usa.cloud.watchguard.com/rest/platform/accounts/v1/accounts/<<account_id>>\",\"method\":\"get\",\"payload\":{\"fields\":\"contacts,addresses\"},\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.watchguard_token>>\",\"Content-Type\":\"application/json\",\"WatchGuard-API-Key\":\"<<CREDENTIAL.watchguard_api_key>>\"}}","position":{"x":1065,"y":-810},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":null,"createdFromTemplateGuid":null,"createdFromTemplateVersion":null}],"links":[],"diagramNotes":[]}
```

## Watch: Simplify security alerts with AI in Tines

From transforming data with a prompt and generated code, to directly accessing and using a language model in your workflows, our AI features make automation even more accessible and efficient for anyone in your organization. Learn how you can take multi-source security alert workflows from 25 actions to 3, reducing the potential for error and making it simpler to make updates like adding sources, change rule definitions, and more.

[Watch video](https://fast.wistia.net/embed/iframe/zz99digy66)

## Watch: Streamline software auditing processes in IT

From transforming data with a prompt and generated code, to directly accessing and using a language model in your workflows, our AI features make automation even more accessible and efficient for anyone in your organization. Learn how AI can streamline software auditing processes in IT with a focus on automating manual tasks like analyzing computer inventories and software installations.

[Watch video](https://fast.wistia.net/embed/iframe/yoqt6vzani)

## Webhook Best Practice: Configuring Names and Paths

Properly naming and configuring the path of your webhooks is a critical best practice to ensure your workflows are maintainable and easy to track.

Read the [Introduction to Webhooks](https://explained.tines.com/en/articles/14007076-introduction-to-webhooks?q=formula) article for more information.

## Why naming conventions matter

A webhook's path becomes part of the URL used for communication with external systems. Failing to configure a meaningful name can lead to tracking issues and unnecessary work, especially when the webhook URL is placed in third-party applications.

**Consequences of poor naming:**

-   **Difficulty tracking:** If a webhook is created and takes a nonsensical path based on a temporary story name (e.g. `new_story_for_user_7086`), it becomes difficult to track where the URL leads.
    
-   **Inconvenience:** If you later rename the story, or need to update the path, you must manually go to every third-party tool where that webhook URL is used and update it as well.
    

## Best practices for webhook configuration

1.  **Use a meaningful path  
    **The path you set for your webhook is part of the URL it generates. This path should be descriptive of the webhook's function.
    
    -   **Example:** Instead of a generic or temporary name, a webhook that receives support ticket updates could be named `support-notifications`. This immediately informs you of the webhook's purpose when viewing the URL.
        
2.  **Name the action itself  
    **Always name the webhook action within your story (e.g., do not leave it as `Webhook Action`).
    
3.  **Use webhook rules**  
    Webhook Rules allow you to specify criteria that must be met in order for an event to be emitted. Read more about webhook rules in our docs, [here](https://www.tines.com/docs/actions/types/webhook/#webhook-rules).
    
4.  **Prioritize sensible configuration  
    **Even if the webhook is used as the entry point for a send to story action, where the path is not relevant to the invocation, it is still considered good practice to configure a sensible path and name the action. This maintains a good habit, particularly for stories that may be invoked both via send to story and direct webhook calls.
    

Read more about webhooks in our docs, [here](https://www.tines.com/docs/actions/types/webhook/).

## What access does a user with a viewer role in Tines have to credentials?

Users with a viewer role have the ability to "look" at the credential by clicking it under "Credentials" in the team, so they could look at how a credential is configured They can only see \*\*\*\*\*\*\*\* for any sensitive data.

> **👉 For more details on teams and roles [see here](https://www.tines.com/docs/admin/teams)**

## What are actions in Tines?

Actions are the building blocks of all Tines automation stories. Actions can perform a number of functions, including interaction with 3rd-party tools and services. Once you drag an action onto the storyboard in Tines, you have started building your first story in Tines!

Actions emit [events](https://intercom.help/tines-9c531f867f99/en/articles/6925442-what-are-events-in-tines) to other actions. Actions can either run on a predefined schedule or when they receive an event.

[Watch video](https://fast.wistia.net/embed/iframe/f0ggpbu6n3)

> Please note: We recently introduced our eighth action type, the AI Action. This video is being updated. In the meantime, you can learn about the eighth action and our AI-powered features [here](https://www.youtube.com/watch?v=7FlKch137mE&ab_channel=Tines).

There are eight types of actions. Each action type:

-   is designed to perform a specific action.
    
-   has unique configuration options.
    
-   can receive and emit events to other action types.
    

Below is a brief description of each action type.

| **Action Type** | **Function** |
| --- | --- |
| Send Email Action | The Send Email action sends emails to recipients specified in the action options |
| Event Transformation Action | The Event Transformation action has several modes that modify the contents of received events |
| HTTP Request Action | The HTTP Request action sends HTTP requests using a variety of methods to a specified url |
| Receive Email Action | 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. |
| Condition Action | The Condition action compares the contents of a field from an incoming Event with predefined rules, when the rules match, an event emit is triggered |
| Webhook Action | The Webhook action will emit events it receives through Webhooks (HTTP callbacks) |
| Send to Story Action | The Send to Story action sends events to another Tines story (the sub-story). After the sub-story has completed its action, the Send to Story action will emit an event. |
| AI Action | The AI Action invokes a secure and private large language model (LLM) on demand. |

> 👉 For a more detailed description of each action type [see here](https://www.tines.com/docs/actions).

## What are events in Tines?

When an action runs, it completes its job and passes data to the next action in your workflow in what we call an event. The number of events emitted by a particular action is shown in a pill in the top right of each action. For example, here we see that each action in this story for sending daily jokes in an email have 3 emitted events each.

![](https://downloads.intercomcdn.com/i/o/677225689/69d27f9214c71e18f3b90ced/1dab5f01-8088-4a6c-b3e8-4db0dd8e01df.jpg?expires=1784851200&signature=7202e0e1d488c4e825c463cf4271b31cb73e7cc8008e780c1c9ca619a1fc224f&req=cicgFMt7m4lWFb4V1XW4gQMHEfSR7eEdsKtUipyWkyu7OFEUrO6U48orPCfp%0AgsIXEyROfej%2BKbDZcxutaoCxMQ%3D%3D%0A)

Under the events tab you'll be able to delete or re-emit events, as well as view the details of each event:

![](https://downloads.intercomcdn.com/i/o/677227693/d8f85e7b460bc647dacff3e3/e9a454cc-03d9-4f37-b495-d96ae43367aa.jpg?expires=1784851200&signature=ee0b83170b0441c761c3675374e1521d7f4a2660657bb96acdbd0a3f9403d86f&req=cicgFMt5m4hcFb4V1XW4gQ%2Bzn3lJHgCoxAXX0ONLLtZYWIUdr5t2IKYRG1aU%0A0W9gtIBo37J1xEFEEp1qAKuKgQ%3D%3D%0A)

Events emitted from all actions upstream will be contained in the receiving action  
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 the calculation of key performance metrics.

> **👉 For a more detailed description of events [see here](https://www.tines.com/docs/events).**

## What are pages in Tines?

Pages made in Tines are web pages connected to your stories that your end users can provide input to (and view output from) your workflows. You can use pages to do things like:

-   **[Kick off a workflow](https://www.tines.com/docs/pages/collecting-input-with-forms)** 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/pages/building-apps-and-flows)**.

[Watch video](https://www.youtube.com/embed/g3q-1Rgny5c?rel=0)

### Page collections

Page collections create a single view of multiple apps for your team.

![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/897267045/58d5096b508a56ca2f59f1e6/1701331989-page-collections.jpg?expires=1784851200&signature=8129e37a1992309fbc102427b192fdd965e525cedae5e0bf7e9831959500af41&req=fCkgFM95nYVaFb4V1XW4gf9iMPiyDSJ2vywZseGenaitSWU6TNVeFMMMw42n%0Asx2GwdiriQZqUQ%2BekuqXufEsJQ%3D%3D%0A)

**You can create a page collection from directly in your storyboard:**

1.  Click the page you’d like to add to a collection
    
2.  Via the build pane, choose “+” next to Collections
    
3.  Choose from an existing collection or create a new one
    

Your collection will then show in the collections section of the menu.

> **👉 Learn more about pages and page collections [here](https://www.tines.com/docs/pages).**

## What are stories in Tines?

Stories are a collection of interconnected actions working towards a singular goal. Think of them like automated use-cases, or playbooks. For example, you might have a Phishing story, an infected endpoint story, or an employee onboarding story.

To begin your first story, simply drag an actions or a template onto your storyboard and connect them like this:

![](https://downloads.intercomcdn.com/i/o/677256282/6a4bbfbca5eb9fd2df27f95d/97f38bbb-56f7-4e30-bdfc-041abfa6995c.gif?expires=1784851200&signature=9f75f1d0cb9cdea1a9e6c3783bff9c89ff40c82ee2fb510ded8180e2b50c5fbf&req=cicgFMx4n4ldFb4V1XW4gYvBy2ZgdHtOnAM%2BRinS65tohoRN9QK%2Fx8HiCNRP%0AwFGuorj5NYqsAc1llmyJR%2Bnefg%3D%3D%0A)

You can also create your first story by importing one from our [Story Library](https://www.tines.com/story-library) from right inside Tines, like this:

![](https://downloads.intercomcdn.com/i/o/677262652/8b71115c628128c1bcddef93/d603a6c9-8924-4f30-b3a9-a5a238c15008.gif?expires=1784851200&signature=c827647470fa9f325a78b87e488fe4eba1fca52f876c6443d6afb4e993977577&req=cicgFM98m4RdFb4V1XW4gWfWAlfpFkC0ft8Ck0N1n3prx7jZK6FQQTeT2TtT%0A5LgJM9so14gCtJm3nrJ%2BhpWl4A%3D%3D%0A)

Another way to get started with stories is with the default stories available to you when you first create your Tines tenant:

-   **The Simple Story**: A short story that demonstrates how actions and events interact to automate workflows.
    
-   **Simple Story - VPN Notification**: This story demonstrates how automated threat detection and response can be performed on corporate VPN connections.
    
-   **Analyze URL in URLScan**: This story demonstrates how Tines actions can be used to analyze suspicious URLs.
    

So you can better understand what a completed story might look like, here is an example of a story designed to automate threat detection and response of corporate VPN connections:

![](https://tines-f60bb36f92a5.intercom-attachments-1.com/i/o/645864102/4d53b5963485cb5eb88f6735/1654856057-quickstart_story-7171552b5404f4622813b966a19384a1.png?expires=1784851200&signature=d7bc6a7f374d5389adb6241a06a147901cf1ce0c1abad053cd13fdaa5ae83f8f&req=ciQiHs96nIFdFb4V1XW4gZ5whZ5ebhYqNGMrff1qhP8FLY8m60SLG%2FZBMkpW%0AYLpkkW2Dn103Ep3Ja8qSAH0Y4w%3D%3D%0A)

When you start creating a new story, it is unpublished by default. An unpublished story is similar to a draft. It will be restricted in that it will only run while you're working on it. Unpublished stories do **not** count toward your stories quota.

> **👉 For more detailed docs on how stories work, [see here](https://www.tines.com/docs/stories).**

## What happens to a Tines API key when the user account that created it leaves the tenant?

When a user creates a Tines API key, there are several levels of access they can set during creation:

-   **Tenant owner:** Access to the entire tenant
    
-   **Team:** Role-based access to a specific team on the tenant
    
-   **Service:** Access to [tenant permissions](https://www.tines.com/docs/admin/user-administration/#tenant-permissions)
    
-   **Personal:** A key tied to your identity and permissions
    

> **Note:** The level of access the user can select at the time of the API key creation also depends on the user's permissions within the [tenant](https://www.tines.com/docs/admin/user-administration/#permissions)/[team](https://www.tines.com/docs/admin/teams/):

Let's look at an example. We have a user named Jane Tino, who, within their tenant, is a tenant owner and also a [team admin](https://www.tines.com/docs/admin/teams/#_1-team-admin) of "Team Automation". This would give Jane access to all four access levels mentioned above.

Within Team Automation, Jane creates Tines API keys for each level as [credentials](https://www.tines.com/docs/credentials/).

-   If Jane's account is active in the tenant, all four API keys will work as intended.
    
-   If Jane's account exists in the tenant but is deactivated, the *Personal API key* will break (returns a `401` status code), while the other three will still work as expected. If Jane's account is re-activated, the Personal API key will continue working again.
    
-   If Jane's account is completely deleted from the tenant, the *Personal API key* will break (returns a `401` status code), while the other three will still work as expected.
    
-   If Jane is reinvited back to the tenant, the *Personal API key* will not work (returns a `401` status code), while the other three will still work as expected.
    

In summary, when a user is deactivated or removed entirely from the tenant, the only API key that stops working is their Personal API key. Even if they are re-invited to the tenant, the Personal API key from before will still not work.

## What IPs are used for Run Python Script executions?

The IPs for [Run Python Script executions](https://www.tines.com/docs/actions/templates/run-python-script/) are not the same as the tenant's egress IPs. These separate IPs are assigned from the AWS IP pools and the range is determined by AWS. Lambda functions within a Run Python Script all use the default VPC owned by the Lambda service, which is mentioned [here](https://docs.aws.amazon.com/lambda/latest/dg/foundation-networking.html).  
Due to this limitation, we don't recommend making HTTP requests from Run Python Scripts (and instead recommend using the [HTTP request action)](https://www.tines.com/docs/actions/types/http-request/).

> **Tines Tip:** To learn more about how the Run Python Script works behind the scenes, read more [here](https://www.tines.com/docs/actions/templates/run-python-script/#behind-the-scenes).

## What is a dead job in Tines?

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 not retried again.

> **👉 To understand more about job management, [see here](https://www.tines.com/docs/admin/job-management).**

## What is a flow in Tines?

A flow is a connected set of one or more elements on the Storyboard, that can run automatically. We define a autonomous flow as one that can run by itself without someone opening the story and clicking "Run".

In other words:

-   2 or more connected elements that can run autonomously
    
    -   containing an IMAP, webhook, form
        
-   1 or more scheduled elements
    
    -   scheduling enabled on at least one action
        

Elements include any of the 8 actions, a tool, a page or template you can pull onto the Storyboard to begin a workflow. This does not include annotations.

You can have one or more flows on a Storyboard, but will be charged for each autonomous flow. Flows located in your Personal team (formerly known as Drafts) cannot be scheduled and therefore cannot be run automatically. These flows will not be charged. See Tines pricing and packaging [here](https://explained.tines.com/en/articles/9620399-understanding-tines-pricing-and-packaging) for additional information on our pricing model.

This is an example of a single flow:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1432832855/2161aa3a63aba61687792b83906a/single+flow+example.png?expires=1784786400&signature=8da71b5b119e0765f5618c41c1816521dd9af5d7853c07ba35a5398c1149ab44&req=dSQkFMF9n4laXPMW3nq%2BgUlvNURXYYlGdiRGrq736d3MVAaXSVKzn3QTnCbh%0AAzcA1AIXdc38uZbs5c%2BIHT1XIaI%3D%0A)

This is an example of 2 flows:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1432833461/511d47d4a89cfcb45ebd451d955b/example+flow2.png?expires=1784786400&signature=4de0885932e60ade5ace4549e7d34a0f2d72a4bd0c1218b47f531f56d9e001ab&req=dSQkFMF9noVZWPMW3nq%2BgZzh7nWBwOk1DrcU9DTKafB%2FNwCF2BpsjOFO1Ly6%0ALhwSqDQ6MJVEoP6Ayh%2Fc5vvPgpc%3D%0A)

## Limits

Below are the limits on the number of flows you can have in your Tines tenant.

| **Edition** | **Flow limits** |
| --- | --- |
| Community | 3 |
| Business | Starting at 30 |
| Enterprise | Contact Tines |

Read how usage in Tines is calculated [here](https://explained.tines.com/en/articles/9653918-how-usage-in-tines-is-calculated).

> 👉 For more detailed information on how flows work, reach out to your Tines account team.

## What is AI in Tines?

All AI features in Tines are powered by secure access to tenant-scoped large language models (LLMs). There are two core features and our newest product, Workbench, behind AI in Tines.

## Features

### **AI Agent action**

Bring advanced AI capabilities directly into your workflows with the AI Agent action's two modes, **Task** and **Chat**. Automate complex tasks, improve decision-making, and enable natural, dynamic interactions with integrated systems.

### **Automatic mode in Event Transform**

Author data transformations with all the power of code, without the need to read or write it. Simply provide your input and describe the output you need in plain English. Once you approve the behavior suggested by AI, it’s locked into the workflow for consistent execution. Perfect for processing complex, gnarly data structures. Great for deterministic behavior.

### **Workbench**

A natural language chat interface to interact with data and take action on your workflows, privately and securely. Workbench removes the need to switch context between applications. It offers builders a singular view to run and monitor in the context of real-time scenarios or events. Learn more about Workbench [here](https://explained.tines.com/en/articles/9822635-introduction-to-workbench#h_be9fbf76c1).

### **Workbench for Storyboard (formerly Story copilot)**

An AI-powered chat interface built directly into the Tines storyboard. With its two modes, ask and chat, Workbench for Storyboard helps you build, understand, maintain, and optimize stories using natural language — whether you're starting from scratch or working with an existing workflow. It makes Tines accessible to your whole team, regardless of technical skill level.

## **Get inspired**

Below are some examples of where each feature would best suits your workflows.

### **AI Agent action**

**Task mode:**

-   Condense multi-thread support conversations into a status update with next steps.
    
-   Investigate URLs, file hashes, or email headers and return a summary of potential threats using integrated tools.
    
-   Assess the tone, language, or structure of alerts and assign a severity level.
    

**Chat mode:**

-   Help employees understand and follow internal policies (e.g., PTO, travel, or security) by answering questions in real time.
    
-   Walk users through diagnostic steps for common issues (like VPN connection failures) and offer next actions or escalation.
    
-   Gather preferences from a user in conversation and use tools to find and propose open times via integrated calendar services.
    

### **Automatic mode**

-   Process/convert any data structure
    
-   Statistically analyze event data
    
-   Map an incoming alert to a record schema
    
-   Convert between file formats
    
-   Perform advanced cryptography
    
-   Add a watermark to an image
    

### Workbench for Storyboard

-   "I didn't build this story — can you explain it to me?"
    
-   "My story isn't running the way I want it to. Can you help me figure out what's wrong?"
    
-   "We changed from X tool to Y tool. Help me update the credentials."
    
-   "I'm a new builder. Build me a flow that checks IPs against a threat feed and blocks malicious ones."
    

### **Workbench**

-   Send a message in Slack
    
-   Look up an employee in BambooHR
    
-   Create a ticket in Jira
    
-   Get detections in CrowdStrike Falcon
    
-   Lock down a device in Jamf
    

## **How do I turn on or off the AI-powered features?**

We believe the AI-powered features are extremely powerful in improving your overall efficiency as builders and problem solvers. While we hold firm on that belief, we also understand that your company might have policies or processes for opting into AI features.

To allow for this, the tenant owner can turn off or on the AI-powered features tenant-wide. They can also allocate AI Agent action credits to different teams in their tenant to manage their usage.

### **Turn on or off**

Follow these steps to turn on all AI-powered features:

1.  Go to the **Settings** drop-down in the top left of your screen.
    
2.  Choose AI Settings.
    
3.  Check the box to turn AI on; uncheck it to turn AI off.
    

[Embedded content](https://demo.arcade.software/Ja9xMqPpAsgHWFVjIDQR?embed)

### **Automatic mode in the Event Transform action**

Automatic mode in the Event Transform action is available to all plans and does not require AI run-time credits or team allocation. After turning it on, it's available to all teams in your tenant.

### **Distributing** AI run-time **credits for the AI Agent action**

The AI Agent action requires you to distribute AI run-time credits in addition to turning the feature on. To allocate credits to one or multiple teams, follow these steps:

1.  Go to the AI settings screen
    
2.  Check the box next to the team(s) who should have credits
    
3.  Specify the credit volume for each team
    
4.  Click 'Save distributions'
    

> **👉 Learn more about AI Agent action monthly credits [here](https://explained.tines.com/en/articles/9369092-how-ai-usage-and-ai-credits-work-in-tines).**

## **Talk to your team**

Want to introduce AI in Tines features but need help pitching to your internal teams? Use [this guide](https://explained.tines.com/en/articles/10389755-how-to-pitch-ai-in-tines-to-your-grc-team) to learn what questions to expect and get set up for success.

## **Continue learning about AI in Tines**

-   [Security & privacy for AI in Tines](https://explained.tines.com/en/articles/9369078-security-privacy-for-ai-in-tines)
    
-   [How AI usage and AI credits work in Tines](https://explained.tines.com/en/articles/9369092-how-ai-usage-and-ai-credits-work-in-tines)
    
-   [Writing effective prompts](https://explained.tines.com/en/articles/12097316-writing-effective-prompts)
    
-   [Best practices for Automatic mode](https://explained.tines.com/en/articles/9369122-best-practices-for-automatic-mode)
    
-   [Best practices for the AI Agent action](https://explained.tines.com/en/articles/11644147-best-practices-for-the-ai-agent-action)
    
-   [Workbench articles and best practices](https://explained.tines.com/en/collections/10398899-workbench)
    
-   [Introduction to Workbench for Storyboard](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot)
    
-   [Workbench for Storyboard: Best practice guide](https://explained.tines.com/en/articles/13713921-story-copilot-best-practice-guide)
    
-   Tines Docs:
    
    -   [AI Agent action](https://www.tines.com/docs/actions/types/ai-action/)
        
    -   [Automatic mode](https://www.tines.com/docs/actions/types/event-transformation/automatic/)
        
    -   [Workbench](https://www.tines.com/docs/workbench/)
        
-   Tines University: [Using AI in Tines](https://www.tines.com/university/tines-basics/using-ai-in-tines/)

## What is my Tines cloud egress IP address?

The egress IP is the IP address that external servers or services see as the source of an HTTP Request action request.

To see the egress IP addresses for your tenant, visit `https://<tenant-domain>/info`.

> **Tip:** The tenant egress IP does not apply to our Run Python Script. To learn more about the IPs used for our Run Python Script, read [here](https://explained.tines.com/en/articles/10470380-what-ips-are-used-for-run-python-script-executions).

## What is the grading turnaround time for Tines Certification submissions?

The Tines Training Team generally delivers grading responses within a 72-hour time frame following your exam submission.

> **Tines Tip:** To sign up for a Tines Certification, visit our [registration page](https://www.tines.com/get-certified).

## What is the session timeout for SSO in Tines?

You can configure SSO using OIDC and SAML under "Authentication Settings".

When set up, you can choose the "Session timeout".

![](https://downloads.intercomcdn.com/i/o/785127609/11e374da4af8e7d7d48f60c3/Screenshot+2023-07-14+at+11.55.20.png?expires=1784851200&signature=a54863d32700e1e27a5bbc269c181d41da995a3060b2f6a502df2cdb6ab0ec91&req=cygiF8t5m4FWFb4V1XW4gXEsPQcgir3BadWEIvxmahRXanmr56PERX1hF4RB%0A3T4DDL2t%2BuVqO%2FCif9MlHd%2BPFg%3D%3D%0A)

> **To learn more about SSO in Tines, [see here.](https://explained.tines.com/en/articles/6883934-single-sign-on-sso)**

## What is the size limit for an individual event?

There is a 100MB limit on the size of an event.

> **👉 For more details on events [see here](https://www.tines.com/docs/events).**

## What kind of problems can I solve with Tines?

Companies across the world are saving thousands of hours by automating their workflows using Tines.

Below are ***just some*** of the ways in which our customers have turned the power of Tines into tangible business results.

#### Security teams use Tines to automatically:

-   [Analyse phishing emails](https://www.tines.com/story-library?s=Phishing+email)
    
-   [Triage security alerts](https://www.tines.com/story-library?s=security+alerts)
    
-   [Analyse files](https://www.tines.com/story-library?s=analyze+file)
    
-   [Add form data to spreadsheets](https://www.tines.com/story-library?s=add+form+data)
    
-   ***[...and many more](https://www.tines.com/story-library/)***
    

#### IT Ops teams use Tines to automatically:

-   [Create Jira issues using Slack](https://www.tines.com/story-library/87653/create-jira-issues-via-slack)
    
-   [Create chatbots for communicating within and between remote teams](https://www.tines.com/blog/chatbots-for-security-and-it-teams)
    
-   [Suspend or disable accounts in Google, Okta, Github](https://www.tines.com/story-library/87650/suspend-or-disable-accounts-in-google-workspace-github-and-okta)
    

#### DevOps teams use Tines to automatically:

-   [Handle Github notifications and notify Slack](https://www.tines.com/story-library/1136336/handle-github-notifications-and-notify-slack)
    
-   [Reimage an AWS instance](https://www.tines.com/story-library/87635/reimage-aws-instance)
    
-   [Create Terraform infrastructure as code](https://www.tines.com/story-library/89549/create-terraform-infrastructure-as-code)
    

#### HR teams using Tines to automatically

-   [Onboard employees & grant specialized access to specific tools](https://www.tines.com/story-library/1120478/onboard-employees-grant-specialized-access-to-specific-tools)
    
-   [Add new employees to Bamboo HR and manage system access](https://www.tines.com/story-library/87651/add-new-employees-to-bamboohr-and-manage-system-access)
    

> If you've got an idea for a Story in Tines, **[we'd love to hear it!](https://www.tines.com/story-library?tag=IT&modal=request-story)**

## What's an event in Tines?

When an action runs, it completes its job and passes data to the next action in your workflow in what we call an event.

Each time an action executes and emits an event, it counts toward your event limit for your story/tenant. Event limits are measured daily or monthly depending on your plan. You can learn more about this [here](#h_31c17139ea).

The number of events emitted by a particular action is shown in a pill in the top right of each action. For example, here we see that each action in this story for sending daily jokes in an email have 3 emitted events making a total of 12 events in the story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802449050/6b4985b99585572354ff3ad0837e/1dab5f01-8088-4a6c-b3e8-4db0dd8e01df.jpg?expires=1784786400&signature=959f6dd932373bd89096669486159e20e25490648c16a1977f786a5d55275670&req=dSgnFM16lIFaWfMW3nq%2BgbmZFmTveLWd4Z5gdC04lzyhwNPcuB8bo%2B2tMg19%0Aub49HGJZdCeDbVk3X%2FTnxtWw7mc%3D%0A)

Under the events tab you'll be able to delete or re-emit events, as well as view the details of each event:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802449048/d5cfb37c7f966023292b9f222d1a/e9a454cc-03d9-4f37-b495-d96ae43367aa.jpg?expires=1784786400&signature=71d470acc1473d86a8af4e188d0bca81cfd6a56ef62217f19e60387c178dde52&req=dSgnFM16lIFbUfMW3nq%2BgSgX6XkE8WBKRCaK7%2FMATxo4QiaS6OFC2lM%2Fu5XY%0AoHlrzKvFsNOdRMOZi0r2sIIL%2F%2Bw%3D%0A)

Events emitted from all actions upstream will be contained in the receiving action. There is a 100MB limit on the size of an event. For more details on events [see here](https://www.tines.com/docs/events).

#### Event retention

By default, Tines retains event and log data for seven days. **Depending on your Tines subscription**, you can configure how long event and log data are stored in Tines. Open your story, and on the right panel, select the desired option from "Keep events and logs."

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802451768/0fad9c981ba886daa4805e6e2f8c/Screenshot%2B2023-05-03%2Bat%2B15_22_50.png?expires=1784786400&signature=dfe17668aa65b716fccf9d00179ab71908fa70d5c927b6a4c8756ed19cb291fd&req=dSgnFM17nIZZUfMW3nq%2BgR4NEzIk0byPGsjIXJFrbJtM37Ie1hGtshAhAEOk%0AWzsMnlGjBlMDTXcE8rVy130Qapk%3D%0A)

Then select how long you would like to store the data for:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1802451770/fabe16225fe81dafa16dcdf08e27/Screenshot%2B2023-05-03%2Bat%2B15_23_13.png?expires=1784786400&signature=649810290d415e907900d1524865aa3e59b731048e6ea5a18a0380ff33432aef&req=dSgnFM17nIZYWfMW3nq%2BgcmB8HO1TbWPsPTK9nmfscvlDi93E8Lyqxkef03p%0AgGF8mQcKLhrt%2B8lZuR75rhHdLS0%3D%0A)

  
If the period of retention has passed, the events cannot be recovered.

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 the calculation of key performance metrics.

## Limits

Below are the limits on the number of events you can have in your Tines tenant.

| Edition | Event limits |
| --- | --- |
| Community | 25,000 monthly |
| Business | 1,500,000 daily |
| Enterprise | Contact Tines |

Read how usage in Tines is calculated [here](https://explained.tines.com/en/articles/9653918-how-usage-in-tines-is-calculated).

> 👉 For a more detailed description of events [see here](https://www.tines.com/docs/events).

## What timezone are requests made in the audit logs returned?

Audit logs in Tines are stored in UTC timezone and ISO8601 format

e.g. 2023-04-21T10:30:02+00:00

## What Tines AI features are available to community edition users?

All of our AI features and products are available for Tines community edition users. This includes:

-   Automatic mode for Event Transform
    
-   AI Agent action
    
-   Workbench
    
-   Workbench for Storyboard (formerly Story copilot)
    

## **Credits**

Community edition tenants will have 50 AI run-time credits to use each month.

> For more info on run time credits go [here](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits).

For Workbench, tenant admins can interact with the chat interface on their tenant at up to 20 daily messages tenant-wide. Workbench does not consume any AI credits.

> **👉** For more information on Automatic mode, [see here](https://www.tines.com/docs/actions/types/event-transformation/automatic).
>
> 👉 For more information on Workbench, [see here](https://www.tines.com/docs/workbench/).
>
> 👉 For more information on AI Agent action, [see here](https://www.tines.com/docs/actions/types/ai-agent/).
>
> 👉 For more information on Workbench for Storyboard, [see here](https://www.tines.com/docs/stories/story-copilot/).

## When a user is deleted in Tines, what happens to their stories, credentials, and resources?

Stories, credentials, and resources saved in a team will remain available.

The deleted user's drafts will be deleted. Any shared credentials in those drafts will also be deleted.

## When do AI credits reset in my Tines tenant?

AI credits reset on the first of the month.

> **Tines Tip:** Explore [AI in Tines](https://explained.tines.com/en/collections/9473753-ai-in-tines) to learn more about our AI offerings.

## Where can I download Tines' logo?

As an example, when creating an app in your 3rd party platform to connect to Tines, attaching our logo helps to visually identify Tines in your toolkit. You can access our brand assets by following these steps:

1.  Navigate to the [Tines website](https://www.tines.com/).
    
2.  Right-click on the Tines logo in the top-left corner.
    
3.  Select **Access all Tines brand assets**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384083691/be9e1d1c3da4569aa145e803bc85/image.png?expires=1784851200&signature=00cb23a03a53c8f2fc78a5145aa183c891a9f85c8e14403995b28321aef8f7df&req=dSMvEsl2nodWWPMW3nq%2BgXXxAAlTrOAetU4BTUX87kafaaHQ0QN3RyvBGEuE%0ACZg6oNZheEQxSu2U65pvidiVbTs%3D%0A)
    
4.  Click **Download brand essentials**:
    
    ![](https://downloads.intercomcdn.com/i/o/am0g137l/1384086668/9c5a828ad5d368c12c33b2637ffa/image.png?expires=1784851200&signature=0b70f70aad0117f3af15e26ea856861fa555817a6696c01546f88a13fb35161b&req=dSMvEsl2m4dZUfMW3nq%2BgUapisaqAugMVQ5AQtp1LMNtLWLePSogT6j0YStv%0Aqb6yfxc4qwr%2BcFxIdW2aW4fMfIU%3D%0A)
    

This will locally download a ZIP file named **Tines Brand Essentials** that contains AI, PNG, and SVG files of our full logo and icon.

## Where can I find Tines' customer Data Processing Agreement (DPA)?

Tines' Data Processing Agreement (DPA) can be found on our website here: [Customer data processing agreement](https://www.tines.com/dpa).

## Where can I request to have a user added as a tenant owner?

Tines can promote ***existing*** users to tenant owners for your tenant. Contact [Tines Support](https://www.tines.com/contact) and provide the following information:

-   The full tenant URL. (i.e. `https://your-tenant-domain.tines.com`)
    
-   The user's email tied to the account within the tenant.
    
-   Someone within the tenant (it cannot be the requester) will need to provide us with a secondary approval for this change. They can send an email to [support@tines.com](mailto:support@tines.com) that states “I approve (user's email here) to be added as a tenant owner."
    

Once we have obtained this information, our team can begin processing your request. Requests can typically take up to 48 business hours to complete.

> **Tines Tip:** For more information around tenant owners and other roles, read more here: [User administration](https://www.tines.com/docs/admin/user-administration)

## Where do I log in to my Tines Certification dashboard?

To access your Tines Certification dashboard, log in here: [Tines Certification Login](https://certification.tines.com/login)

> **Tines Tip:** To sign up for a Tines Certification, visit our [registration page](https://www.tines.com/get-certified).

## Where do I turn in my Tines Certification exam submissions?

Once you have run through your exam story and ensured there are no errors, you can submit your Tines Certification Lab submission within the certification itself:

-   **Tines Core Certification**: Within "Exercise #" sections of the Certification Practical Exam module:
    

![](https://downloads.intercomcdn.com/i/o/1056685209/c6d69472f257b14c0dc2d467/image.png?expires=1784851200&signature=7755969b5f68a75b22ead49f7862c814f8cd3dbaa6542114c0cfe6fc26395810&req=dSAiEM92mINfUPMW3nq%2BgaMhld%2F9r9HbR%2FWsVd%2FrmW4agXdcqL2kjZNZIYqn%0AanUaOJ4hcHmPEjvyi%2BVKeG9XZOk%3D%0A)

-   **Tines Advanced Certification**: Within the "Exam: Submission" section of each module:
    

![](https://downloads.intercomcdn.com/i/o/1056690030/19eb5bf4611a43fead8bcb26/image.png?expires=1784851200&signature=5fde7ff45834c0d87d181ebf0f9e8433feb297de191da26e62c7bab362008e86&req=dSAiEM93nYFcWfMW3nq%2BgQH7vp2EhsJSl8S%2FZBjK9sPp5%2BWRZl2u8se%2FiGwp%0AkxD28oLhWp%2BcbK96lzWZDOJoF68%3D%0A)

> **Tines Tip:** To sign up for a Tines Certification, visit our [registration page](https://www.tines.com/get-certified).

## Which AWS regions does Tines Cloud currently support?

We currently host Tines in the following AWS regions:

-   `ap-northeast-1` (Tokyo)
    
-   `ap-southeast-2`(Sydney)
    
-   `eu-west-1` (Dublin) - EU Community Tenants are hosted here
    
-   `eu-west-2` (London)
    
-   `eu-north-1` (Stockholm)
    
-   `eu-central-2` (Zurich)
    
-   `us-west-2` (Oregon) - US Community Tenants are hosted here

## Who can receive email notifcations regarding tenant limits?

To receive an email regarding your tenant, you must be a tenant owner.

> **If you would like to learn more about teams and users in Tines, [click here](https://explained.tines.com/en/articles/6883947-using-teams-in-tines).**

## Who can set up SSO on Tines?

To configure SSO, you must be a tenant admin or owner. By default, the tenant owner is the person who created the tenant and is the first tenant admin. There can be multiple tenant admins.

> **To learn more about teams and roles, [see here](https://intercom.help/tines-9c531f867f99/en/articles/6883947-using-teams-in-tines)**
>
> **To learn more about SSO, [see here](https://explained.tines.com/en/articles/6883934-single-sign-on-sso)**

## Why have I not received my sign-in email from Tines?

Emails from Tines originate from the following 4 addresses

-   [support@tines.com](mailto:support@tines.com)
    
-   [support@tines.io](mailto:support@tines.io)
    
-   [mail@tines.com](mailto:mail@tines.com)
    
-   [mail@tines.io](mailto:mail@tines.io)
    

We advise that you add these emails to your email safe sender or trusted list.

## Wiz Authentication Guide

Wiz is the leading cloud infrastructure security platform that enables organizations to rapidly identify and remove the most pressing risks in the cloud. Wiz uses an agentless approach—a single API connector per cloud and Kubernetes environment to scan deep within every cloud resource.

There are two ways to integrate with Wiz in Tines:

-   [Calling the Wiz APIs from Tines](#h_5a703edbbd)
    
-   [Receiving Webhooks from Wiz in Tines](#h_7f6d37aa09)
    

## Calling the Wiz APIs from Tines

**Wiz connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Wiz" and follow the prompts to connect.
    

## Receiving Webhooks from Wiz in Tines

### First, create a Webhook action in Tines

1.  Create a new Story
    
2.  Drag a "Webhook" action onto the storyboard  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/931403864/71601a3f717921c9235ec35a/image.png?expires=1784851200&signature=6ab5dfcbeee35599cf6ff9afb850f991f2db95b4069d52ff14b20653a04a4f2a&req=fSMmEsl9lYdbFb4V1XW4gWO2pE8he0fCNx2If60REpL1Sqhf%2Bb9ECD%2F%2BGa8Q%0AdAhGMUoML0BBhEKHNO72sC7cZQ%3D%3D%0A)
    
      
    ​
    
3.  In the "Build" tab on the configuration panel, copy the "Webhook URL"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/931404417/2f5be0f85c488ae5e8436cb5/image.png?expires=1784851200&signature=3be18cb5f06fd10123bfd643374c5b33ac842d5ad09c2cb599ffcf765904a0c8&req=fSMmEsl6mYBYFb4V1XW4gWdqW12LO0%2B937pWDbm3t%2F9y1Bqb93s%2FU87AjAus%0A4mSYErpE2uLBCCVpWpPk9ufXbQ%3D%3D%0A)
    
      
    ​
    
4.  Add a Tines integration to your Wiz tenant (see [Wiz Documentation](https://docs.wiz.io/wiz-docs/docs/tines-integration#add-a-tines-integration-in-wiz))
    
5.  Once the Tines integration is configured, create a Tines Push integration in Wiz using the Webhook URL copied earlier (see [Tines Push integration](https://docs.wiz.io/wiz-docs/docs/tines-integration#push-integration-steps))
    

### Lastly, test the integration

1.  Manually trigger an action on an issue in Wiz for the Tines Integration created (see [Run an action on an issue](https://docs.wiz.io/wiz-docs/docs/issues#run-an-action-on-an-issue))
    
2.  Open your story in Tines.
    
3.  If your integration is correctly configured, you will receive a green notification with the number 1  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/931406830/b1b0bceecfb16cd7fa3e6fe4/image.png?expires=1784851200&signature=607de64d009c6571749c31d2d7e11b4ba94e339bdfc8910753153ff9b599dee3&req=fSMmEsl4lYJfFb4V1XW4gXubWdPvXg9l5OVCDgiDm%2Fz6QI%2F1J6yUNhQFtObu%0AywlIPp4kfPxpRZiI%2FNheD%2Bhq%2BQ%3D%3D%0A)
    
      
    ​
    
4.  (Recommended) After confirming the integration is working, add an Automation Rule in Wiz to automatically send webhooks to Tines for Wiz Issues, Vulnerabilities, Cloud Configuration Findings, etc. when they are generated (see [Add an automation rule](https://docs.wiz.io/wiz-docs/docs/tines-integration#optional-add-an-automation-rule))
    

> **Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/wiz).**
>
> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of Wiz stories in the [story library](https://www.tines.com/library?view=all&s=wiz).**

## Workbench FAQs

## **What is Workbench?**

Workbench is a familiar Tines-powered AI chat interface that can access proprietary information, take action, or identify next steps in real-time across your tech stack. All interactions through Workbench must come from an authorized user’s instruction or approval to take action.

## **What types of actions can Workbench take?**

Workbench interacts with any stories or action templates you give it permission to access. As it is user-scoped, the access level depends on the user's access-level to the stories and elements. To read more about using send-to-story with Workbench, [click here](https://explained.tines.com/en/articles/9855926-using-stories-with-workbench).

## **How do I know where to start?**

Start by interacting with a familiar tool like Jira, Slack, or CrowdStrike. Click the template, then choose from various available tasks, such as sending Slack messages, creating tickets in Jira, or getting detections in CrowdStrike Falcon.

If you need inspiration, check out some examples from our Labs team in [our Library](https://www.tines.com/library/product-features/workbench/).

## **Do I have to be a Tines customer to use Workbench?**

No! Workbench is available for all editions of Tines. To get the most out of it, we recommend using it in combination with existing stories. It is not available as a standalone product.

## **Can I use Workbench without paying for it?**

Yes. Workbench is available in a limited capacity for free, learn more [here](https://explained.tines.com/en/articles/9855931-how-to-access-workbench).

## **How do I enable my AI-powered features to access Workbench?**

[This explained article](https://explained.tines.com/en/articles/9396326-how-do-i-turn-on-or-off-the-ai-powered-features) will walk you through how to enable AI-powered features, including Workbench, in Tines.

## **Will my Workbench chats be reflected across my tenant or specific to a team?**

Workbench chats are user-scoped. The contents of a chat are private to the individual user; audit logs are kept for actions taken and messages sent.

The templates and stories you interact with are available based on the teams you’re in. Any story or credential available to “all teams & drafts” will be available to all workbench users.

## **Does Tines capture an audit log of the tasks Workbench performs?**

All actions are captured in the platform's audit logs section, which are available to tenant admins via our UI and API. Our system automatically and consistently captures a log for each user request and Workbench response based on the story or template used. Read more on audit logs in our [docs](https://www.tines.com/api/audit-logs/).

## **What stories and credentials can I access from Workbench?**

Workbench is scoped to the individual user; therefore, within Workbench, users can enable credentials or stories they have access to run within Tines. To understand more, check out our [docs](http://tines.com/docs/workbench/).

## **Will stories used with Workbench count towards a tenant’s story limits?**

Stories used *exclusively* for Workbench do not count towards any story limits as they cannot run autonomously. Learn more about [stories and Workbench here](https://explained.tines.com/en/articles/9855926-using-stories-with-workbench).

## **Is there a token limit when messaging Workbench?**

Our text limit is measured in tokens, where conversations are limited to 200K tokens. Limits can be hit by either:

-   Having a large quantity of tools enabled
    
-   Generating a massive output from a tool call
    
-   Generating a massive input to a tool call (i.e. file data)
    

## How are my daily Workbench messages counted?

All Tines plans, including community plans, include 20 daily messages. These messages are available to tenant admins only.

For unpaid Workbench plans, the daily Workbench messages are counted and reset on a 24-hour rolling period.

For example, a message entered at 11 a.m. counts against the limit of 20 messages until 11 a.m. the next day.

## **Is Workbench available on mobile and tablet?**

Yes! Workbench is available for users on mobile devices (Android, Apple), tablets, and desktop computers.

## **Is Workbench available for self-hosted tenants?**

Yes, consistent with all AI in Tines, Workbench is available for cloud and self-hosted tenants. Our [self-hosted docs](https://www.tines.com/docs/self-hosting/additional-configuration/ai/) provide information on configuring and connecting to different AI providers for self-hosted tenants.

## **What is Workbench good at?**

It’s great at assisting you as you work in real-time, whether that’s aggregating data from your proprietary systems, investigating alerts, or taking actions. In addition, like any other AI-powered chat, it has powerful general knowledge capabilities.

## **What is it not good at?**

While you can use it to build actions, stories, or resources in Tines, we still believe that stories are best built by those who know them best.

> Bookmark our [Workbench docs](http://tines.com/docs/workbench/) and [Introduction to Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench) for all available resources on Workbench.

## Workbench for Storyboard: Best practice guide

Workbench for Storyboard (formerly Story copilot) is a conversational AI interface in Tines to build, understand, and optimize stories. This article outlines key recommendations to help you use Workbench for Storyboard.

Read our Introduction to Workbench for Storyboard article [here](https://explained.tines.com/en/articles/13572150-introduction-to-story-copilot).

There are two main modes in Workbench for Storyboard:

-   **Ask** - Ask anything about the story on screen or general Tines queries
    
-   **Build** - Build, edit or debug your workflow
    

## Ask mode

Ask mode is designed purely to help you understand and assess existing Tines stories or ask general Tines queries. Its primary goal is to provide clarity and insight into any story on your screen.

#### Example: Need to understand a complex story

-   **Prompt**: “This is a big story, can you summarize it for me?”
    
-   **Result**: Workbench for Storyboard will summarize the story, give you information such as its purpose, overview, key components, integration points, any issues and some notable features being used.
    

#### Example: Reviewing a critical story for maintenance.

-   **Prompt**: “Wanting a health check on my story. Are there any updates I should make?”
    
-   **Result**: Workbench for Storyboard will analyze the configuration of the actions in the story, check credentials, check action logs for errors, and suggest specific changes.
    

### Tips

-   **Query safely**: Use Ask mode when querying a story to prevent accidental edits.
    
-   **Access**: if you don't have editor access to storyboards, you will only see Ask mode.
    

## Build mode

Build mode is the comprehensive interface for all workflow construction, modification, and troubleshooting. It functions as the umbrella for three core capabilities: build, edit, and debug.

> **Note:** Build, edit and debug are not separate modes you have to select, they all live under Build mode.

### Build

This allows you to create a story from scratch.

#### Example**:** Create a story that triages an alert and sends a notification if its critical

-   **Prompt:** “I have an alert that follows this format:
    

{

“alert”’ {

“user\_name”: “tino”,

“ip”: “194.88.246.242”,

“email”: “[tino@tinesdemos.com](mailto:tino@tinesdemos.com):,

“device”: “MacBook Pro 16”

}}

Build out a story that receives the alert, triages it, and emails the team if it’s critical”

-   **Result:** Workbench for Storyboard will analyze the request to plan the workflow. It may ask you some confirming questions first like “Which threat intelligence service would you like to use for IP reputation checks?”. It will search existing templates to see if one exists already. If there is one, it will give you a diagram of the workflow just before it starts to build it. Once it has built the story, it will also test the workflow to make sure it works. Workbench for Storyboard will then summarize the story for you and you will have the option to review and accept or refuse the suggested story.
    

#### Example: A beginner user attempting their first workflow.

-   **Prompt:** “I’m a new builder and need help getting started. I want to build a flow that checks a list of IPs against a threat intelligence feed and blocks them if they are malicious.”
    
-   **Result:** Workbench for Storyboard will ask a few confirming questions before drafting the workflow, explaining the steps for a new user, and possibly offering to guide them through the configuration.
    

### Edit

This allows you to make changes to an existing story.

#### Example: Adding a final notification step to a story.

-   **Prompt:** “When the entire vulnerability remediation story finishes running successfully, can you add a step to post a summary of the results to the #security-ops Slack channel with the tag @security-lead?”
    
-   **Result:** Workbench for Storyboard will add a new Slack Send Message Action at the end of the workflow, configure it to post the summary, and set up the necessary conditional logic.
    

#### Example: Preparing a finished story for team handover or production.

-   **Prompt:** “I finished building the offboarding story. Can you rename and write descriptions for all the actions to improve clarity?”
    
-   **Result:** Workbench for Storyboard will rename all actions with clear, descriptive titles (e.g., 'HTTP Request to HR System' to 'Fetch Employee Manager') and add a concise description to each action explaining its purpose.
    

### Debug

This can help you figure out why a story isn’t working.

#### Example**:** Messages not being sent to Slack

-   **Prompt:** “Why isn’t my slack message sending?”
    
-   **Result:** Workbench for Storyboard will debug the issue for you and when it finds the issue, it will communicate it back to you and explain why it is not working. Next, it will fix the issue and give you the option to review and accept or refuse the change.
    

#### Example: A story is failing with an unknown error.

-   **Prompt:** “My story isn't running the way I want it to. I expected it to block the IP address, but it only sent the email. Can you help me figure out what’s wrong?”
    
-   **Result:** Workbench for Storyboard will analyze the story's execution history, identify the point of failure or divergence, and explain the issue, offering a fix.
    

### Tips

-   **Be specific and concise**: Provide succinct but detailed prompts to ensure Workbench for Storyboard accurately understands your goal.
    
-   **Review all changes:** Always review and approve Workbench for Storyboard's suggested actions to maintain full control over your workflow.
    

Read this article on [Writing effective prompts](https://explained.tines.com/en/articles/12097316-writing-effective-prompts) for more help.

## Using your own style guides

To build or customize stories using your team's internal style guides and best practices, you can create a Workbench for Storyboard guidelines resource.

This article will help you create [resources in Tines](https://explained.tines.com/en/articles/12691570-resources-in-tines).

To leverage your resource, simply ask Workbench for Storyboard to reference the resource when you are building. This makes team-wide use and updates easy as your internal best practices evolve.

> Read more about Workbench for Storyboard [here](https://www.tines.com/docs/stories/story-copilot/).

## Workbench for Storyboard now uses AI credits

Workbench for Storyboard (formerly known as Story copilot) now consumes credits from your monthly AI credit allocation, alongside the AI agent action and Workbench.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2423055040/87555b1fcf6ff83816414684d4cf/CleanShot%2B2026-05-25%2Bat%2B17_05_21-402x.png?expires=1784786400&signature=4daf9b4aafb9dc13a2d57ab3c498dc0af2f37b40cf18469c03aec3cba93c2fce&req=diQlFcl7mIFbWfMW3nq%2BgaXl%2F%2B74uW0Qumjzh5Jnb2hlM%2Bji2p1rq%2BQB1ist%0ATrxWSsrk1n8qGzLV7y%2FLP0i9JPk%3D%0A)

To support this transition, we're increasing every customer's monthly credit capacity by a minimum of 25%.

The following features help you manage consumption:

-   **Notifications** at 80% and 100% usage for tenant owners and team admins.
    
-   **Credit distribution** to reserve credits for specific teams and mission-critical workflows.
    
-   **Workbench for Storyboard limits** to control how much each team can spend on Workbench for Storyboard.
    
-   **Top-up credits** as a safety net if you exhaust your allocation.
    

> Note: Read our [AI usage and credits](https://explained.tines.com/en/articles/12801399-ai-usage-and-credits) article for help with any of the above features.

You can set Workbench for Storyboard limits per team in your AI credit allocations settings: `<tenant>.tines.com/settings/ai/allocations?tab=workbench_for_storyboard`

Check your updated credit amount in tenant settings, or visit the reporting page to review your current usage. [Read more on our blog](https://www.tines.com/blog/bringing-story-copilot-into-the-ai-credit-framework/).

## Writing effective prompts

Using AI in Tines, you can take action and access proprietary data with [Workbench](https://www.tines.com/docs/workbench/), and with the [AI agent action](https://www.tines.com/docs/actions/types/ai-action/), bring advanced AI capabilities directly into your workflows.

> **Note:** AI in Tines uses AI run-time credits. You can see the real-time AI run-time credits used per team in the **AI settings** section of the tenant **Settings** menu. Learn more about AI run-time credits [here](https://explained.tines.com/en/articles/9369092-ai-action-credits-executions).

Creating effective prompts for AI, or prompt engineering, can seem like a daunting task at the outset, but by focusing on writing clearly and concisely you’ll be able to leverage the most out of AI.

Following these 5 tips will help you to write better prompts, and get better results faster.

1.  Write clearly and provide specific details
    
2.  Provide context and use personas
    
3.  Be specific about outputs and constraints
    
4.  Clearly explain your goal and success criteria
    
5.  Refine and adjust your prompts iteratively
    

#### **1\. Write clearly and provide specific details**

Structure your prompt logically with clear instructions and include specific examples of what you're looking for. Avoid vague requests and instead provide concrete details about your expectations. When writing your prompt, use plain language to explain what the task is.

❌ Vague prompt:

```
Help me analyze this log file.
```

✅ Clear, structured prompt:

```
I need to analyze this Cisco firewall log for potential security incidents.
Please:
1. Identify any failed login attempts (more than 3 from the same IP)
2. Flag any connections to known malicious IPs (especially those from countries X, Y, Z)
3. Summarize the findings in a table with columns for IP, event type, and recommended action
4. Include a brief explanation of the security implications
```

#### **2\. Provide context and use personas**

Give the AI relevant background information where necessary and consider asking it to adopt a specific role or perspective. When using personas in AI, it’s better to tell it that it is something, rather than to pretend that it is something. This will help to structure the response in a way that aligns with your needs.

❌ Limited context:

```
Write an email about the security incident.
```

✅ Rich context with persona:

```
You are an experienced CISO communicating with the executive team. 
Write an email about yesterday's ransomware incident that:

1. Summarizes what happened (encrypted files in the finance department)
2. Explains current containment status (isolated to one subnet, backup restoration in progress)
3. Outlines next steps (security audit, employee training)
4. Maintains a calm, professional tone while conveying appropriate urgency
5. Keeps the email under 300 words for readability
```

#### **3\. Be specific about outputs and constraints**

Clearly define what format you want the output in, any limitations to consider, and for complex tasks, ask the AI to show its reasoning process. Being specific with these details will help to reduce the amount of back and forth clarifications needed. However, you don’t want to make your request too busy or complex, be specific but succinct where you can.

❌ Ambiguous request:

```
Analyze this network traffic data.
```

✅ Specific request with output format:

```
Analyze the attached network traffic data for potential data exfiltration attempts.

Constraints:
- Focus only on outbound traffic to non-corporate domains
- Consider any single connection transferring >100MB as suspicious
- Flag connections occurring outside business hours (8am-6pm local time)

Please show your analysis step by step, and present your findings as:
1. A markdown table of suspicious connections with timestamp, destination, data volume
2. A brief explanation of why each flagged connection is concerning
3. Recommended next steps for investigation
```

#### **4\. Clearly explain your goal and success criteria**

Help the AI understand what you're ultimately trying to accomplish and how you'll evaluate whether its response meets your needs. The more clear and direct you are with a goal the better. When you’re clear on this before writing your prompt, it’ll make understanding whether the response is right, or where it’s gone wrong much easier.

❌ Goal-less request:

```
Create a script to check our AWS environment.
```

✅ Goal-oriented request:

```
I need to create a Python script that audits our AWS environment for compliance with our security policy. 

The goal is to identify and remediate any S3 buckets with public access enabled.

The script should:
1. List all S3 buckets in our account
2. Check each bucket's permission settings
3. Generate a report of non-compliant buckets
4. Include remediation commands to fix each issue

A successful script will correctly identify all public buckets in our test environment and provides accurate remediation steps that can be executed without manual modification.
```

#### **5\. Refine and adjust your prompts iteratively**

Treat prompt writing as an iterative process. If you don't get the desired results, provide feedback and refine your prompt based on what worked and what didn't.

❌ Initial prompt:

```
Help me create a dashboard to monitor security alerts.
```

✅ Refined prompt after initial response:

```
Thanks for the overview. I'd like to create a more specific security alert dashboard in Tines. 

I need to visualize:
1. Alert volume by severity (Critical, High, Medium, Low)
2. Top 10 alert sources
3. Alert trend over the past 7 days

Can you help me with the specific Tines formulas I'll need to aggregate this data from our alert JSON?
```

**Learn more:**

-   [Introduction to Workbench](https://explained.tines.com/en/articles/9822635-introduction-to-workbench)
    
-   [Frequently asked questions: Workbench](https://explained.tines.com/en/articles/9855941-frequently-asked-questions-workbench)
    
-   [Get started with Agents](https://explained.tines.com/en/articles/11541238-get-started-with-agents)
    
-   [AI Agent action FAQs](https://explained.tines.com/en/articles/11510164-ai-agent-action-faqs)

## Writing formulas with Workbench

Workbench is able to get the latest documentation about formulas to help you write both simple and complex formulas.

The best way to get started is to provide Workbench with some sample data aexplain what you're trying to achieve using Tines formulas. Workbench will then fetch the latest Tines documentation on formulas to suggest a combination of formulas to solve your ask.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1404803934/491d394b01a725aa5a45992dfeb2/Formula.png?expires=1784851200&signature=febdc49d4899c5600f9bf0aa3d8c4c416bc5fd1df63a937b5cac146890915458&req=dSQnEsF%2BnohcXfMW3nq%2BgV2C5PC7VROPfcg8KijnqaBc53ynzSSDPOP8wzZ3%0AQN1dVNVXT%2FhIPchM7lng8d5aPeE%3D%0A)

Like all AI tools, sometimes Workbench will generate a formula that is incorrect. If that happens, you can provide the error message back to Workbench and reexplain what you were trying to achieve and it can correct itself.

## Your first visit to Tines

[Watch video](https://fast.wistia.net/embed/iframe/bmwlrbcvmf)

In this article, we’ll guide you through your very first visit to Tines. Once you've read it, why not [jump on a **1:1 call** with a Tines product expert](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained).

First, on the left, you have your actions. These are the building blocks of all automation in Tines. Using any combination of these eight action types, you can arrange the tasks or steps for any process you automate. The **eight** types are: webhook, HTTP request, receive email, send email, AI, trigger, event transform, and send to Story.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1216389657/b6ef8762aa48f33241d75ca7d325/CleanShot+2024-10-15+at+11_08_13.png?expires=1784786400&signature=0c2c51a69aca8923909a89727a325e178a0b18d230bab3e270c4db10654c0458&req=dSImEMp2lIdaXvMW3nq%2Bge8CTjr01WtO7xmBUDsc9ZHLHKH%2BVcmTwt5WWtxU%0AozTGIELV5eKNg0EUgbBnmaDpKy8%3D%0A)

And, if you aren’t sure where to start, you can drag on one of these pre-configured actions from our action templates, here:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1401163456/81f423761ad7215ecb1f09a8ab56/Actions.gif?expires=1784786400&signature=1ea1846faa0893a1de69930c1fb237b2bcbab160a8ef53a361795893d1f3d394&req=dSQnF8h4noVaX%2FMW3nq%2Bgb%2BQ1ZmErJQ7PVH7URepkFus0h2e3J51un1e%2Ff%2Bv%0A1ji%2FzeWrX6Ld04fTuzU3A6O97cI%3D%0A)

For example: if you wanted to automatically post a Slack message to your team every time a user makes a request, select Slack from templates list and drag the pre-formatted Action onto your storyboard. With the Slack action selected, search for `Send a message` in the properties panel on the right. Connect it to the preceding action, configure it, and that's it.

The area in the center is your storyboard. This is where you’ll build your story by dragging on actions and connecting them to other actions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1401164031/92a406d408c5d335450e4847bba8/Storyboard.gif?expires=1784786400&signature=59bb92ac62b6d93c2a50f07e5eb266cfca231178c655d2107d28b7c47ddb4119&req=dSQnF8h4mYFcWPMW3nq%2BgUpLUmKhxfnVP7oV5smU21OYAtMnP8vgJrupJOkt%0At3GSDZp6GjjApeQ5Atax2YoV4Ec%3D%0A)

> Simply put, **stories** are a series of connected **actions**.

And a story runs when an event passes data between actions. An event is the output of an executed action.

If you're looking for inspiration, consider building one of these stories:

-   request a list of users in a system
    
-   check to see if a URL is malicious
    
-   send some kind of notification (like email) when something specific happens
    

Lastly, this right-hand side panel is called the build panel. As you drag an action onto the storyboard, it changes to reflect what configuration options we have available for that action. For every action, we can give it a name and description and then set the rules by which it makes decisions.

![](https://downloads.intercomcdn.com/i/o/675730018/55881e6ddfb289ec9bd9dfcc/cbbc4e01-d4f6-44c7-a6e6-b91597c4252a.gif?expires=1784786400&signature=48c52b816a48bf59c4eb13f6375e1641bc516dc39ce16a10d6e0d284105df80c&req=ciciEcp%2BnYBXFb4V1XW4gXTh2kEsqG325xnqkVyzjxkaptz7%2F7XuMSYGStuv%0AyDEqhw1%2BFDetdlJk9f81Axv36w%3D%3D%0A)

If you are looking for some inspiration to get your first story going, check out the Tines [story library right here.](https://www.tines.com/story-library)

You’ll see how other stories have been built. And, you can easily import them into your tenant to get going... like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/1401166799/c2457f948b6970e1f74a117e15c1/Story+library.gif?expires=1784786400&signature=1c5df5da129351ba709061ca2110f727f4e0d86c25be56342d202eaaa3b16258&req=dSQnF8h4m4ZWUPMW3nq%2BgVF47sebmvTeuQDQpXkB9vc%2FZYmdtDfCubmtS%2BIU%0APXseAoPRM0%2BnUZwm2BN8ZYn6V%2Fc%3D%0A)

Start building right now –and if you get stuck, chat with us using the messenger inside the product… we’re always here to help.

## Talk to a product expert

What to expect:

-   Zero-pressure, technical demo of the Tines platform
    
-   Walkthrough of a relevant use case
    
-   Overview of products and plans
    
-   Insight into API and integration capabilities
    
    [Get a demo](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained)

## Your first visit to Tines

In this article, we'll guide you through your very first visit to Tines. Once you've read it, why not [jump on a 1:1 call with a Tines product expert](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained).

[Watch video](https://fast.wistia.net/embed/iframe/bmwlrbcvmf)

### **Actions**

First, on the left, you have the story toolbar. This is where you will find the building blocks of all automation in Tines. Using any combination of these action types, you can arrange the tasks or steps for any process you automate. The action types are: webhook, HTTP request, receive email, send email, AI, condition, event transform, and send to story. Also found in the story toolbar are notes, sections and pages.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332266861/af1d9d9f1f64111ab2a0428fc301/toolbar+location2.gif?expires=1784786400&signature=620fb86dcb4f15d5b963e5a5fb394b7590ca20e22f78dd802b603b515b4efdd9&req=diMkFMt4m4lZWPMW3nq%2BgU14aKAG0uh5bjhlWJ%2B4HhVjilx%2Fz7eJwA8CsZxL%0AteOVbjZULHMAWR2VLpHAZDXzbNQ%3D%0A)

And, if you aren’t sure where to start, you can drag on one of these pre-configured actions from the HTTP request.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2332283233/3eb2709fe6a0b2617a845434cbc7/toolbar+templates2.gif?expires=1784786400&signature=164b3f85505dd674a432c2f991d8f0e2f4f0db117d2623c354f76e4e4e2b8067&req=diMkFMt2noNcWvMW3nq%2BgetMfdB6tPsQkUhbjrcwnXW1XqNo%2Fj7Zdinwtz7O%0A8ouSnRJLqPzPtKleYp3uTq5AExU%3D%0A)

For example: if you wanted to automatically post a Slack message to your team every time a user makes a request, select Slack from products list, search for `Send a message` and drag the pre-formatted action onto your storyboard. Connect it to the preceding action, configure it, and that's it.

> **Tip:** You can switch between light and dark modes by clicking on your name in the top right of the screen, and selecting your preferred option.

### **Storyboard**

The area in the center is your storyboard. This is where you’ll build your story by dragging on actions and connecting them to other actions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/2179824367/5dace121e042bb5c96eda82c5ac2/CleanShot+2026-03-19+at+15_33_22.gif?expires=1784786400&signature=9907381d119ce34d5097aa8e64c2d76121860595b647090423f3925e8b60fcf7&req=diEgH8F8mYJZXvMW3nq%2BgZlJUlbnR0SrcLIa4MJGY04XZ1xl2csnYnlkRke%2F%0AAay3i%2FYee3NISLJovmO%2FfFsHZuU%3D%0A)

> Simply put, **stories** are a series of connected **actions**.

And a story runs when an event passes data between actions. An event is the output of an executed action.

If you're looking for inspiration, consider building one of these stories:

-   request a list of users in a system
    
-   check to see if a URL is malicious
    
-   send some kind of notification (like email) when something specific happens
    

### Properties panel

Lastly, this right-hand side panel is called the properties panel. As you drag an action onto the storyboard, it changes to reflect what configuration options we have available for that action. For every action, we can give it a name and description and then set the rules by which it makes decisions.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1801456650/9e62dad7533c956ece914f1c7e20/cbbc4e01-d4f6-44c7-a6e6-b91597c4252a.gif?expires=1784786400&signature=17cd733b9301328ecb7647c87a6afaa86adc93bbaa4b4091665bbc8f7053b2eb&req=dSgnF817m4daWfMW3nq%2BgY7eRurtZP2qo74oo3gjrnZaC8ShE1of0CZLnNFT%0ACwOzSAM3yHCCzJ7zUb42hgeJyds%3D%0A)

### Story library

If you are looking for some inspiration to get your first story going, check out the Tines [story library right here.](https://www.tines.com/story-library)

You’ll see how other stories have been built. And, you can easily import them into your tenant to get going... like this:

![](https://downloads.intercomcdn.com/i/o/am0g137l/2179852200/c28a9e879da0147c316b50624cc1/Clipboard-20260319-154124-509.gif?expires=1784786400&signature=2194c3d40256c3ada97fbb20477b78717d8876e214b54d3438638647cb8b7d6f&req=diEgH8F7n4NfWfMW3nq%2Bgf9nHBn5LYoC%2Fq5VVZZO1tLqFUczghdEAeII3cKN%0APIDjGGrkfL4%2BbASiH6q6F97hrtM%3D%0A)

### **How to switch between tenants**

If you have multiple tenants, you can switch between them using the tenant switcher in your menu in the top left.

![](https://downloads.intercomcdn.com/i/o/am0g137l/1801477309/67f7312243d9b4e172c2e2aaade4/CleanShot+2025-10-28+at+19_23_49.gif?expires=1784786400&signature=18437138e7056f601fdfe78d11ab7e2e0c9580cfc41bed1fe09239590ae8beba&req=dSgnF815moJfUPMW3nq%2BgfHF3pefL%2BYFMNNgLZWvCc4iXJizyyWM%2FdJPNXla%0ALj%2BxDIEvERAopeptbjbuahLBIXI%3D%0A)

### **Talk to a product expert**

What to expect:

-   Zero-pressure, technical demo of the Tines platform
    
-   Walkthrough of a relevant use case
    
-   Overview of products and plans
    
-   Insight into API and integration capabilities
    
    [Get a demo](https://www.tines.com/book-a-demo?utm_source=intercom&utm_medium=product&utm_campaign=explained)

## Zendesk Authentication Guide

Zendesk is a comprehensive customer service and engagement platform designed to empower businesses to deliver exceptional support experiences. With its suite of powerful tools and features, Zendesk enables companies to streamline customer interactions, boost efficiency, and foster customer satisfaction.

This guide covers 2 options for authentication:

1.  [OAuth](#h_96dcc6cf2d)
    
2.  [Basic Authentication](#h_5a6da933e9)
    

## OAuth

1.  Log in to your Zendesk account
    
2.  Click on the application tray and click "Admin Centre"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239510/6890db597310c4dcc17d43d6/image.png?expires=1784851200&signature=88f6ef241ff63998b730f6f0d914502c4ecbff95e0db5bfa42cd5147fc94375e&req=fSEnFMp3mIBfFb4V1XW4gdPeMyV0UshkU2jr9%2F%2BC3g3bF4ZQRGQyWOVxSBmq%0AmkkHI9w%2FzFHmEGzmMH2atV2HqA%3D%3D%0A)
    
      
    ​
    
3.  Click "Apps and Integrations"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239681/ce39de9a6abddd58429c6b62/image.png?expires=1784851200&signature=08a888d0fdb3e5bf13d780e58cb8343be08201276f2a7c45f1ed704fd4b2b749&req=fSEnFMp3m4leFb4V1XW4gRalsrbjQX3LWBTI5D%2B8a%2BwtugoRknKhY3NOqQ5z%0ABBsJGVjJijiQIFxETF1vlwQUdw%3D%3D%0A)
    
      
    ​
    
4.  Click "Zendesk API" under "APIs"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239933/0340c3f7222ccf1a51587c2b/image.png?expires=1784851200&signature=983e6584dfe0afccede5f1610486f2d57361ba664243fbe781aac29e9924580e&req=fSEnFMp3lIJcFb4V1XW4gaG%2FRdk9vsF%2F0gj%2BHVOdgPBV2wEbOyjNHQRZap0H%0AzMDTb%2BewxCzIZpJoHsV7zf%2Flrg%3D%3D%0A)
    
5.  Click "OAuth Clients" and then click "Add OAuth client"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910242743/4d98c6b06c5af357e0c91440/image.png?expires=1784851200&signature=0c702535ffc832974511c70a985c487f17caf5b1d70cf101704aed91fb1b22ab&req=fSEnFM18moVcFb4V1XW4gfI53UDiYmgWgjLLKUCA0jLIUoF3MZQAwNPUNydo%0AeE9QFKrjgGFPUcdgdOiO3eIS4g%3D%3D%0A)
    
      
    ​
    
6.  Name your client, give it a description, add a logo and then click "Save"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910243185/f02f93c7bc9a4c847bb0d74b/image.png?expires=1784851200&signature=891f3ac2b32bee22a508b450d67175267ca850e577e78f6ed5aa82abad0826cf&req=fSEnFM19nIlaFb4V1XW4gfAC7uYVpQ%2BTyEN4HZJAMtXIkhxh2y%2B7JZplBIKZ%0A1Vhtdtt%2FWY71wH5mV6L3872vBg%3D%3D%0A)
    
      
    ​  
    ​
    
7.  Click "OK"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910243609/6f17877de61c63cde57ac928/image.png?expires=1784851200&signature=478962e1bb42e458b4bcdb4911aa9ba3239cd60c85b3d33e72088b4bf87e6d3e&req=fSEnFM19m4FWFb4V1XW4gS6CtIQn68s7wLCpvU0XWWqly0zNZUQbsIMif4V5%0Ab04urnPSfVwaZzNAiPmrXiSC3Q%3D%3D%0A)
    
      
    ​
    
8.  Copy your "Unique identifier" and "Secret" to a text editor
    
    ![](https://downloads.intercomcdn.com/i/o/910245295/4b8d6184261eb1a062e1dd39/image.png?expires=1784851200&signature=7c761196a0c6ced67793acbb1fd50993a7628f6707a77fee8566e94e35680d7f&req=fSEnFM17n4haFb4V1XW4gUA7c7Un0uWjhdir5OKAJSUGhMxhj%2BKBY9kjgZ5t%0Aex4%2BNUooJUTdkeHghHoU7hmwdQ%3D%3D%0A)
    
    ​  
    ​
    

## Create a Zendesk resource and credentials in Tines

You will need to create:

-   One Resource: "Zendesk username"
    
-   Two Credentials:
    
    -   Text type: Zendesk password
        
    -   HTTP Request type: Zendesk OAuth
        

##### Resource

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Resource"
    
3.  Click "+ New Resource"
    
4.  Input the values for the Zendesk resource
    
    1.  **Name:** Zendesk username
        
    2.  **Description:** Optional
        
    3.  **Builder:** Your Zendesk username
        
5.  **Optional**
    
    1.  **Access:** What other teams can also use the resource
        
6.  Click "Save resource"
    

##### Text type

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the Zendesk credential
    
    1.  **Name:** Zendesk password
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Zendesk password
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    

##### HTTP Request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the Zendesk credential
    
    1.  **Name:** Zendesk
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://<subdomain>.zendesk.com/oauth/tokens`
        
    4.  **Content-Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload**: Copy the below object and paste into the "Plain code" section:
        
        1.  Replace `<Unique identifier>` with your "Unique identifier" and `organizations:write read` with the client's scopes
            
        
        ```
        {
          "grant_type": "password",
          "client_id": "<Unique identifier>",
          "client_secret": "<<secret>>",
          "username": "<<RESOURCE.zendesk_username>>",
          "password": "<<CREDENTIAL.zendesk_password>>",
          "scope": "organizations:write read"
        }
        ```
        
    7.  **Secret:** Your "Secret"
        
    8.  Click "Run options", navigate to the `access_token` key and double-click on the key:  
        ​  
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/910250228/d42007b3e3932164133976fb/image.png?expires=1784851200&signature=8fc6b2db557385c1218abb85c2458eb26fb673d8017834331c88a67bc4fc4af9&req=fSEnFMx%2Bn4NXFb4V1XW4gdPSKFlhHEJF8MFtzXQ2YAmKXR5GGyE%2BTLqwsa9%2F%0AGYmJwlf59NKb7ShFtyOeat4lIA%3D%3D%0A)
        
    9.  **Location of token from response:** Paste the path to the token in the value pill, `zendesk_oauth.body.access_token`
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:  
​

![](https://downloads.intercomcdn.com/i/o/910249075/9427ecdef826105fc59de966/image.png?expires=1784851200&signature=378849ced2cad0efac6a9e99ee5b40e1754fa7894ddad686b94cf0cfd1d81446&req=fSEnFM13nYZaFb4V1XW4gQr76xqpfSrBJde7KMPPAzfz3dglZQdEuKnrQbKE%0ABEm3gJ%2F659daRhpHxIrGylPvPQ%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/910249132/c542bc064cc4f41a14d99587/image.png?expires=1784851200&signature=13daeeb14ab7724e6eedf1b13723cd979f3b4e963375bae35f158753e588a992&req=fSEnFM13nIJdFb4V1XW4gZFL2k7zp1thLCZqNU%2FP9P6gJImjymMZVuxKKdYF%0A2sJKMJbZ1iHkKLieAlkozoNRIw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/910249289/1b5724c74a2039a43d78ef60/image.png?expires=1784851200&signature=48401b6acbe24f8a1fdb0ad7e7f92118ad137830de3fd29829c04a7272427150&req=fSEnFM13n4lWFb4V1XW4gYJnUvjhywEXvYBFjZ1JFpLfBJJFpFCQfv3iMCW%2F%0AfB65D9xl4YUXPDOtOM%2BkZ%2F1nhw%3D%3D%0A)

## Basic Authentication

### First, get a Zendesk API token

1.  Login to your Zendesk account
    
2.  Click on the application tray and click "Admin Centre"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239510/6890db597310c4dcc17d43d6/image.png?expires=1784851200&signature=88f6ef241ff63998b730f6f0d914502c4ecbff95e0db5bfa42cd5147fc94375e&req=fSEnFMp3mIBfFb4V1XW4gdPeMyV0UshkU2jr9%2F%2BC3g3bF4ZQRGQyWOVxSBmq%0AmkkHI9w%2FzFHmEGzmMH2atV2HqA%3D%3D%0A)
    
      
    ​
    
3.  Click "Apps and Integrations"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239681/ce39de9a6abddd58429c6b62/image.png?expires=1784851200&signature=08a888d0fdb3e5bf13d780e58cb8343be08201276f2a7c45f1ed704fd4b2b749&req=fSEnFMp3m4leFb4V1XW4gRalsrbjQX3LWBTI5D%2B8a%2BwtugoRknKhY3NOqQ5z%0ABBsJGVjJijiQIFxETF1vlwQUdw%3D%3D%0A)
    
      
    ​
    
4.  Click "Zendesk API" under "APIs"  
    ​  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910239933/0340c3f7222ccf1a51587c2b/image.png?expires=1784851200&signature=983e6584dfe0afccede5f1610486f2d57361ba664243fbe781aac29e9924580e&req=fSEnFMp3lIJcFb4V1XW4gaG%2FRdk9vsF%2F0gj%2BHVOdgPBV2wEbOyjNHQRZap0H%0AzMDTb%2BewxCzIZpJoHsV7zf%2Flrg%3D%3D%0A)
    
5.  Enable "Password access" and "Token access", and click "Add API token"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910240458/717fdc84d4f4d45ef57c290e/image.png?expires=1784851200&signature=39b4e607268ba6f1c64e20de748c82122ecb7582ebf1194e798a572a96a0daf8&req=fSEnFM1%2BmYRXFb4V1XW4gcXMH62Z9NHuNQe2xFW%2F%2FFHVzJXwST0AX1nGt79q%0A6kCqbWmWv1UYkkRmZURQuuK%2FrA%3D%3D%0A)
    
      
    ​
    
6.  Name your token, copy your token to a text editor and click "Save"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/910241038/846de3ecaa04ae8bc53bf5fe/image.png?expires=1784851200&signature=1be7ce2f69a03eb883677c4f2a7bfd02845f9a2322a9270a8ac255bf5bd6fec1&req=fSEnFM1%2FnYJXFb4V1XW4gdxQMGibBNuqTofJDwXGdW1EXw3q%2FUeYOZJfetHc%0A8cW5pgs3MhHyY3AFa5GZlZjaWQ%3D%3D%0A)
    

### Lastly, create a Zendesk credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the Zendesk credential
    
    1.  **Name:** Zendesk
        
    2.  **Description:** Optional
        
    3.  **Value:** Your API token
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Zendesk stories in the [story library](https://www.tines.com/story-library?s=zendesk)**

## Zoho Desk Authentication Guide

Zoho Desk is a cloud-based help desk solution from Zoho Corporation, catering to businesses of all sizes. Key features include management of customer support tickets, a customer support portal, contract management and report creation.

## First, create a ZohoDesk application

1.  Login into your ZohoDesk Developer [Portal](https://api-console.zoho.eu/) and click "Get Started"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891057854/1ed04227b3052d27b491d138/image.png?expires=1784851200&signature=f6855e82c240cb40e500352629f42e100ad474038fcc05c5cf0bf381cc3198ef&req=fCkmFsx5lYRbFb4V1XW4gRqP78GyI5qkT0fj3ZsDqsWMCt7vtxg9TJOMqf94%0ABzBG6xLxYJBf%2F9WIEcSeIDBW9g%3D%3D%0A)
    
      
    ​
    
2.  Choose a "Self Client" and click "CREATE NOW"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891058169/93e7692fafa697ffb92de8b0/image.png?expires=1784851200&signature=8ae6600a705bcad8899fa8aaf21a3bae8ae0dc1f9cb6729f604cb21562d5e33c&req=fCkmFsx2nIdWFb4V1XW4gSNegow7MJ6VnPHNW42L4lGu425uSjerEdfR3LWk%0AjigYxgyAYI%2BjQSx8o6JcLRUzcw%3D%3D%0A)
    
      
    ​
    
3.  On the "Client Secret" tab, copy your "Client ID" and "Client Secret" to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891058858/92ae982de0402143a61d3e36/image.png?expires=1784851200&signature=91d4e6c3f388c10e3bebdb4edc919a73a921da9f14176b86b34122df4cd4411f&req=fCkmFsx2lYRXFb4V1XW4gdwYYooAlRO4juH5qZ3x%2FRart420vMGtr5AswHVL%0ASiGVXGsCZvrxzMhQOinhWwRdfQ%3D%3D%0A)
    
      
    ​
    
4.  Click "Generate Code"
    
5.  Choose the appropriate scope, time duration and give the token a description, then click "Create"  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891062067/4e2f4bea06e62d1c66e21435/image.png?expires=1784851200&signature=2b8b417c09b6e74c2bfb0f6391021400ddfc42a9b9095d34a50f5edd65b56430&req=fCkmFs98nYdYFb4V1XW4gWt7w8X%2B2ihPMvKIa4oNUo3%2F3LFWz971i6%2FIKAok%0A%2F0SaYJp9Rvih44%2BBggfei0zMmA%3D%3D%0A)
    
      
    ​
    
6.  Select the appropriate "Portal" and "Organization", then click "Create"
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891062658/a85b2772195f26326a1bfb67/image.png?expires=1784851200&signature=27d19027a5dc140782579d61bdb0c2ceecaa94180970496ab9a8dedfde1d76c5&req=fCkmFs98m4RXFb4V1XW4gSTzkBrur5jR91D0wp1e0y8JAW6ZgXsxAHNR8k3z%0Ah%2FQ%2B1a3uAuPj1CHaGs%2B4hjeRlw%3D%3D%0A)
    
      
    ​
    
7.  Copy your code to a text editor
    
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891063153/b388035e511883d4bc35b911/image.png?expires=1784851200&signature=f4b292ce63b370cdd6d3aafc31af649accb8b30513dfb493ded4bbe0df0832be&req=fCkmFs99nIRcFb4V1XW4gfslzkPrP%2BpCAtUTBZqxZ04GO%2FJJChZE56EoIwmU%0AdDHAKzdbIUKfH8idagMAy65jwQ%3D%3D%0A)
    

## Lastly, create ZohoDesk credentials in Tines

1.  Login to your Tines tenant
    
2.  Create a new story
    
3.  Paste the below curl command onto your storyboard and replace the values for `client_id`, `client_secret` and `code` with your "Client ID", "Client Secret" and "Code"  
    ​
    
    ```
    curl -v \
    -X POST \
    --location \
    "https://accounts.zoho.eu/oauth/v2/token" \
    -H 'Content-Type: form' \
    -d 'code=code&grant_type=authorization_code&client_id=clientid&client_secret=client%20secret&redirect_uri=https%3A%2F%2Fwww.tines.com'
    ```
    
4.  Run the action and copy the value for the `refresh_token` key to a text editor  
      
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/891065517/675138718a48288a708456fc/image.png?expires=1784851200&signature=d4e664035b2a8282254a11bcc205d7fcb924d56dd80e9643f2526573102ca85d&req=fCkmFs97mIBYFb4V1XW4gQemMiniQjykfS42M4Gnvb%2BlkNnBzFt0GPPkOGU5%0AGzetVaHypGCuiRRCPZ3hx03XSw%3D%3D%0A)
    

You will need to create three credentials:

-   Two Text type: "zohodesk\_client\_id" and "zohodesk\_client\_secret"
    
-   One HTTP request type: "zohodesk"
    

##### Text type

1.  Navigate to the team that will be using the API and click "Credentials"
    
2.  Click "+ New Credential" and select "Text"
    
3.  Input the values for the ZohoDesk credential
    
    1.  **Name:** zohodesk\_client\_id
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Client ID
        
4.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
5.  Click "Save"
    
6.  Click "+ New Credential" and select "Text"
    
7.  Input the values for the ZohoDesk credential
    
    1.  **Name:** zohodesk\_client\_secret
        
    2.  **Description:** Optional
        
    3.  **Value:** Your Client secret
        
8.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains.
        
    2.  **Access:** What other teams can also use the API
        
9.  Click "Save"
    

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the ZohoDesk credential
    
    1.  **Name:** ZohoDesk
        
    2.  **Description:** Optional
        
    3.  **URL:** `https://accounts.zoho.eu/oauth/v2/token`
        
    4.  **Content Type:** Form
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:
        
        ```
        {
          "refresh_token": "<<your refresh token>>",
          "client_id": "<<CREDENTIAL.zohodesk_client_id>>",
          "client_secret": "<<CREDENTIAL.zohodesk_client_secret>>",
          "scope": "Desk.tickets.ALL",
          "redirect_uri": "https://www.tines.com",
          "grant_type": "refresh_token"
        }
        ```
        
    7.  **Headers:** `"orgId":"<<your org ID>>"`
        
    8.  Click "Save and run request", navigate to the `access_token` key and double click on the key
        
    9.  **Location of token from response:** Paste the path to the token in the value pill, `zohodesk.body.access_token`
        
3.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
4.  Click "Save"
    

Your credential should look like the following:

![](https://downloads.intercomcdn.com/i/o/891071940/62f87de2b85475111a58ff8f/image.png?expires=1784851200&signature=0fa8964145205e780f6d83169c5d3b0539280d21f5ac4f378bb8c8f5017c0c3b&req=fCkmFs5%2FlIVfFb4V1XW4gUagOiTlUN1PB83rWNvowTxX6G5yBMcKqBtJcw%2Bt%0AN0W2Omp4VPCay3ROsGWSKqMdgw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/891072097/bd89479c4159423d4090e3fa/image.png?expires=1784851200&signature=9e58c0657f265418fd4a2babfd1000aebcb1f2b523dbbf4d109b9742b4019c3e&req=fCkmFs58nYhYFb4V1XW4gSP3OJaYy%2FbN5I%2BCxMVFKsWGxFrH6W7XGZHJ0DLu%0AQx4BCJx7GGAbNTzIY59Imr2l5w%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/891074874/c1ebc90d189d42d43dd266ad/image.png?expires=1784851200&signature=16ecc7c27ab246e8c0766bd2c528807287e49b569aefd3f9a215996c9b4c1fdc&req=fCkmFs56lYZbFb4V1XW4gYHPCGD3BmHhL83TluaPeOCWXVaYebA6qt9jk8xd%0Al1WPIJG4RaDLQzj1WNZeXlt8Nw%3D%3D%0A)

> **More on creating credentials in Tines [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can also find a selection of ZohoDesk stories in the [story library](https://www.tines.com/library?view=all&s=zohodesk).**

## Zoom Authentication Guide

This guide covers 2 types of authentication for Zoom:

1.  [Server to server OAuth app](#h_4965ad46f9)
    
2.  [Personal OAuth app](#h_d2d3f22b86)
    

## Server to server OAuth app

### First, create a Zoom Server-to-Server OAuth app

1.  Navigate to [https://zoom.us/signin](https://zoom.us/signin) and sign in to your account
    
2.  Navigate to [https://marketplace.zoom.us/develop/create](https://marketplace.zoom.us/develop/create) and click "Create" on the "Server-to-Server OAuth" type (see [Zoom's Create a Server-to-Server OAuth app](https://developers.zoom.us/docs/internal-apps/create/))  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/813468845/5df3cc6f0a3b457fdb9a6293/image.png?expires=1784851200&signature=93f61f122fa92bda4874bc77ec1163c68b6063e9fe226eb7d82b598f172b88d4&req=fCEkEs92lYVaFb4V1XW4gbh5Uv3004K6aAXdzv56ogFiuXcBunqj0CS7VVED%0AshmPm71sw9nA2npOx2AFAPkTLQ%3D%3D%0A)
    
      
    ​
    
3.  Name the app and click "Create"
    
4.  Copy the "Account ID", "Client ID" and "Client Secret" values  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/813471000/3fb97406872f07ca433b21ae/image.png?expires=1784851200&signature=c1af3f266da9d22f1686165774363a89cc2e3eb2d4d3e489a0da0fc1bde983d7&req=fCEkEs5%2FnYFfFb4V1XW4ga%2FK1UjYVV%2FZqhIlbL8VuIF%2BSa81XeAm51Mkxusx%0AGxbXA%2BuqIYS9%2BzSA9NxQLXqWgg%3D%3D%0A)
    
      
    ​
    
5.  Click on "Information" and fill out the details
    
6.  Click on "Scopes" and choose the required scopes
    
7.  Click on "Activation" and then click "Activate your app"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/813471776/f1f54f90b29c16420e2f98de/image.png?expires=1784851200&signature=f69fe4a76bdb48b93835b9493272424cc61690a7fa1c53dfb1e5320df05604db&req=fCEkEs5%2FmoZZFb4V1XW4gbMoalD9xZxIe8hapZ42ms0VYoO2e09sCGUnVrup%0Afw6uRsAmWohETow9O6aR1o2jlA%3D%3D%0A)
    

### Next, create Zoom credentials in Tines

**Zoom connect flow (recommended):**

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credentials"
    
3.  Click "+ New Credential" and select "Zoom" and follow the prompts to connect.
    

> Read about our connect flows [here](https://www.tines.com/docs/credentials/connect-flows/zoom).

### Using the credential in an action

The Header configuration for your Zoom S2S credential should be:

```
"Authorization": "Bearer <<CREDENTIAL.zoom_s2s>>"
```

Here is an example Zoom action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"35","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"List all Zoom users","description":"Lists all users in a Zoom account. Query parameters can be added to filter for active/inactive/pending users.","options":"{\"url\":\"https://api.zoom.us/v2/users\",\"content_type\":\"json\",\"method\":\"get\",\"headers\":{\"Authorization\":\"Bearer <<CREDENTIAL.zoom_s2s>>\"}}","position":{"x":180,"y":915},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"544f7d2d06f0501d676fad32aea5526193e59e964a48af3e6f959ee69ee50a46","createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```

## Personal OAuth app

### First, create a Zoom Credential in Tines

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "OAuth 2.0"
    
4.  Copy the "Callback URL"
    

### Next, create a Zoom OAuth app

1.  Navigate to [https://zoom.us/signin](https://zoom.us/signin) and sign in to your account
    
2.  Navigate to [https://marketplace.zoom.us/develop/create](https://marketplace.zoom.us/develop/create) and click "Create" on the "OAuth" type (see [Zoom's Create an OAuth app](https://developers.zoom.us/docs/integrations/create/))  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/804409219/5bc4ef160056b2b04ecb8e12/image.png?expires=1784851200&signature=861b9a0ae18fa09801261dd686e82bb3cbe44f2117cebe08d74632efbf6d0e67&req=fCAjEsl3n4BWFb4V1XW4gWX%2B2M5yikj5cnvqFrwQxlOu7sApTAEJkBkywPtI%0AUSRnLSvLmSQqwF2%2FrLYLv6sq%2Bg%3D%3D%0A)
    
      
    ​
    
3.  Name the app and choose an app type
    
    1.  **Account-level app**: can manage all users in an account and have the ability to perform more actions across the organization
        
    2.  **User-managed app**: do not have to be an admin of your workspace and you can do user-level API calls for your accountfor ch as creating a meeting
        
          
        ​
        
        ![](https://downloads.intercomcdn.com/i/o/804411401/86fbfdf9afa18a611f94e606/image.png?expires=1784851200&signature=77308e64c61574c824b830962cf3546cf860f2cf9769d4810a109d153808a994&req=fCAjEsh%2FmYFeFb4V1XW4gYvM9kJDrAEicdCGbd57BULLlEjQIl95lnfGX247%0A9Gaf2RUmDufMtU96fnaY7Q0XCw%3D%3D%0A)
        
          
        ​
        
4.  Paste the "Callback URL" copied from the Tines credential into the "Redirect URL for OAuth" and "Add allow lists"  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/804416376/bb3b5a7d9a917c028264fa32/image.png?expires=1784851200&signature=8ad519384f70bd4686906259e7556565ac28e49bb86025cf2d77b6d54964ce7e&req=fCAjEsh4noZZFb4V1XW4gbqk%2FPdSloCFRp4TXKyYAd2y7ZaDTsXdpCZ1XTXV%0Azh4UtSAW8gydnrVsp%2B686VvBIw%3D%3D%0A)
    
      
    ​
    
5.  Copy the "Client ID" and "Client secret" values  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/804417013/dcb661332994ed3b36db3bf1/image.png?expires=1784851200&signature=33fe172a640f8bd3ffab9ab496d7ea1e2b79f407211913c7d7f5db5c77822cca&req=fCAjEsh5nYBcFb4V1XW4gUlupofmlqa9%2BtXFa9oMy6DaDNfvnGn6iOOVyGHW%0A%2B5NWEFZUtmG7P2ysdFqep7j73w%3D%3D%0A)
    
      
    ​  
    ​
    
6.  Click on "Information" and fill out the details
    
7.  Click on "Scopes", choose the required scopes and then copy each scope  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/804419089/8707c774f26c5194b0e43af1/image.png?expires=1784851200&signature=deb6da97c49d8037c346b08e66eb4f0fc86db7c30d0e26499011b0755d90ffee&req=fCAjEsh3nYlWFb4V1XW4gZ1DbU%2FIro8I%2FgukkCuEXI%2FPm99%2BmTXgGOKjPpmn%0AVHn6JdT%2FvD0PSZfjgDBAogDpCQ%3D%3D%0A)
    

### Lastly, finish creating the Zoom credential in Tines

1.  Input the values for the Zoom credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Client ID:** "Client ID" copied earlier
        
    4.  **Client secret:** "Client secret" copied earlier
        
    5.  **Scope:** Scopes copied earlier, separated by a space
        
    6.  **OAuth Provider:** Manual
        
    7.  **Grant type:** Authorization code
        
    8.  **OAuth authorization request URL:** `https://zoom.us/oauth/authorize`
        
    9.  **PKCE challenge method:** None
        
    10.  **OAuth token URL:** `https://zoom.us/oauth/token`
         
2.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API  
        ​  
        ​
        
    
    ![](https://downloads.intercomcdn.com/i/o/804421387/f1d8bd793ddb4efca26736e8/image.png?expires=1784851200&signature=b356ae891a20efae7ef256ab65335f73b06946ecf6a09ec72931ce600b7a2e26&req=fCAjEst%2FnolYFb4V1XW4gc6Duppzqn4ig84hetFqZdjUNVVa1RYzAfdwMOnS%0AoGtOE1TbYACWYI7x%2FUurDtCP2g%3D%3D%0A)
    
    ![](https://downloads.intercomcdn.com/i/o/804421578/5e29aa04dc380d2ffc851a5a/image.png?expires=1784851200&signature=b1ee8bc0937cc78119fc5ee38a954bf3940841ff3ffedaa168e32f5a1b7149d2&req=fCAjEst%2FmIZXFb4V1XW4gbUcCDA78FzZ9BwNnp3HGzSkU4t6NNAcfORuW1YN%0AJxZ67bnCEe0LUhtxFEkURAGGVg%3D%3D%0A)
    
      
    ​
    
3.  Click "Save credential" and then click "Allow" to authorize the app  
    ​  
    ​
    
    ![](https://downloads.intercomcdn.com/i/o/804422520/11f0bbc8ff7dbf36b508becf/image.png?expires=1784851200&signature=8f2c416523f2916cb4a62d899e1b24406e57604284c2e778faff56b75d83e8f4&req=fCAjEst8mINfFb4V1XW4gb3%2BgpNFc9j7M4gcEShLS2C5ZDzkHQmVRci4TWCK%0AqHqbbFBxlpjdQwCr5Xro7BX3uQ%3D%3D%0A)
    

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of Zoom stories in the [story library](https://www.tines.com/story-library?s=zoom)**

## ZScaler Authentication Guide

### First, get your API key in ZScaler

1.  Log in to your ZIA Admin Portal.
    
2.  Navigate to Administration > Cloud Service API Key Security and copy the API key.
    
    1.  If one is not present, click "Add API Key" to create one (see [ZScaler Cloud Service API Key](https://help.zscaler.com/zia/about-cloud-service-api-key))
        

### Next, create ZScaler Credentials in Tines

Four credentials will need to be created:

-   Three Text type: "ZScaler Username", "ZScaler User Password", "ZScaler API Key"
    
-   One HTTP request type: "ZScaler"
    

##### Text type

1.  Login to your Tines tenant
    
2.  Navigate to the team that will be using the API and click "Credential"
    
3.  Click "+ New Credential" and select "Text"
    
4.  Input the values for the ZScaler credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **Value:** API Key
        
5.  **Optional**
    
    1.  **Domains:** Ensure this credential can only be used when making HTTP requests to specific domains
        
    2.  **Access:** What other teams can also use the API
        
6.  Click "Save"
    

##### HTTP request type

1.  Click "+ New Credential" and select "HTTP Request"
    
2.  Input the values for the ZScaler credential
    
    1.  **Name:** Required
        
    2.  **Description:** Optional
        
    3.  **URL:** [`https://zsapi.<Zscaler`](https://zsapi.\<Zscaler) `Cloud Name>/api/v1/authenticatedSession`
        
        1.  `<Zscaler Cloud Name>` is the name provisioned for your organization by Zscaler (e.g. `zsapi.zscalerbeta.net`).
            
    4.  **Content Type:** JSON
        
    5.  **Method:** post
        
    6.  **Payload:** Copy the below object and paste into the "Plain code" section:  
        ​  
        ​
        
        ```
        {
        "apiKey": "<<ZSCALER_OBFUSCATE_API_KEY(LOCAL.credential, LOCAL.timestamp)>>",
        "username": "<<CREDENTIAL.zscaler_username>>",
        "password": "<<CREDENTIAL.zscaler_user_password>>",
        "timestamp": "<<LOCAL.timestamp>>"
        }
        ```
        
    7.  **Local values:** Click the "+ Option" button, select "Local values" and paste the below into the "Plain code" section:  
        ​  
        ​
        
        ```
        {
          "timestamp": "<<DATE('now', '%s%L')>>",
          "credential": "<<CREDENTIAL.zscaler_api_key>>"
        }
        ```
        
    8.  Hit Save. Then click "Run options" and make sure you get a successful response.
        
    9.  **Location of token from response:** `SPLIT(zscaler.headers['set-cookie'], ';') |> FIRST(%)`
        
3.  Additional configurations
    
    1.  **URLs and domains:** Ensure this credential can only be used when making HTTP requests to specific domains. Add the base url to the specific domain for all 4 credentials: Zscaler API Key, Zscaler Username, Zscaler User Password, and Zscaler.
        
    2.  **Restrict direct access:** Select this option to prevent the credential from being directly retrieved outside of a story, ensuring it is only used within controlled Tines workflows.
        
    3.  **Access:** What other teams can also use the API.
        
4.  Click "Save"
    

Your credential should look like the following:

  
​

![](https://downloads.intercomcdn.com/i/o/808547909/5c3f83601c20ea26d12d7a2a/image.png?expires=1784786400&signature=92efb44c4a6c4916ee168a40dba71fc0d8412b167a1f48bd9216f8e3cdae0783&req=fCAvE815lIFWFb4V1XW4gWa39Ce7jtkSm%2FZ%2F%2FW26RX2KENeMczeAPf55%2FWIx%0AW2RJUD%2Bcdr66T5Y%2BEdOMw0rNPw%3D%3D%0A)

![](https://downloads.intercomcdn.com/i/o/808548091/cdc433dd51c173552b69f330/image.png?expires=1784786400&signature=973d9c8f7f967ca1c9a7edb2c5b8df156a8d8f2c44f3d24311e20430aba8c1eb&req=fCAvE812nYheFb4V1XW4gWjrtgH7DPn39%2BND%2BgTQH8mNdY7srq70isNTEJvd%0Ar%2BNLbcI75Grt7c2mn8ha%2Bpts9w%3D%3D%0A)

> **For more on creating credentials in Tines, click [here](https://explained.tines.com/en/articles/6877578-what-are-credentials-in-tines).**
>
> **You can find a selection of ZScaler stories in the [story library](https://www.tines.com/story-library?s=zscaler).**

### Using the credential in an action

The Header configuration for your ZScaler credential should be:

```
"cookie": "<<CREDENTIAL.zscaler>>"
```

Here is an example ZScaler action you can copy and paste onto your storyboard in Tines:

```
{"standardLibVersion":"32","actionRuntimeVersion":"4","agents":[{"disabled":false,"name":"Create Admin User","description":"Creates an admin or auditor user","options":"{\"url\":\"https://admin.zscaler.net/api/v1/adminUsers\",\"content_type\":\"application_json\",\"method\":\"post\",\"payload\":{\"loginName\":\"johnsmith@acme.com\",\"userName\":\"John Smith\",\"email\":\"johnsmith@acme.com\",\"role\":{\"id\":2322},\"password\":\"AeQ9E5w8B$\",\"rank\":7,\"name\":\"Read only\",\"policyAccess\":\"READ_ONLY\",\"dashboardAccess\":\"READ_WRITE\",\"reportAccess\":\"READ_WRITE\",\"analysisAccess\":\"READ_ONLY\",\"usernameAccess\":\"READ_ONLY\",\"adminAcctAccess\":\"NONE\",\"permissions\":[\"CUSTOM_URL_CAT\",\"ADVANCED_SETTINGS\",\"COMPLY\",\"FIREWALL_DNS\",\"SECURE\",\"SSL_POLICY\",\"VZEN_CONFIGURATION\",\"PARTNER_INTEGRATION\",\"LOCATIONS\",\"VPN_CREDENTIALS\",\"HOSTED_PAC_FILES\",\"EZ_AGENT_CONFIGURATIONS\",\"SECURE_AGENT_NOTIFICATIONS\",\"AUTHENTICATION_SETTINGS\",\"USER_MANAGEMENT\",\"IDENTITY_PROXY_SETTINGS\",\"APIKEY_MANAGEMENT\",\"OVERRIDE_EXISTING_CAT\",\"REMOTE_ASSISTANCE_MANAGEMENT\"],\"logsLimit\":\"UNRESTRICTED\",\"roleType\":\"ORG_ADMIN\"},\"headers\":{\"cookie\":\"<<CREDENTIAL.zscaler>>\"}}","position":{"x":735,"y":1215},"type":"httpRequest","timeSavedUnit":"minutes","timeSavedValue":0,"monitorAllEvents":false,"monitorFailures":false,"monitorNoEventsEmitted":null,"recordType":null,"recordWriters":[],"form":null,"cardIconName":"httpRequest","createdFromTemplateGuid":"8eed5d2f44abb9a27905037f99400921a997b97b4d2639374ca915aa3ebc4560","createdFromTemplateVersion":null,"originStoryIdentifier":"cloud:aa47f8215c6f30a0dcdb2a36a9f4168e:d4c15df0f02ba4789095426607003199"}],"links":[],"diagramNotes":[]}
```