# List mail triggers

GET

/v1/mail-triggers

```sh
curl --request GET \
  --url 'https://example.com/v1/mail-triggers?limit=25&status=active' \
  --header 'Authorization: Bearer <token>'
```

The workspace’s mail triggers whose project the caller may read; with `projectId`, that project’s only (which needs `read` on it).

## Authorizations

- **[bearerAuth](https://docs.kindgi.com/reference/api.md#bearerauth)**

## Parameters

### Query Parameters

**limit**

integer

default: 25 >= 1 <= 100

1..100. Default 25.

**cursor**

string

Where the previous page ended: its `nextCursor`, as it came. A runtime that seals cursors takes one only for the same list, filters and caller, within a day; otherwise `400 bad-input`, and the list starts again without it. Absent → first page.

**status**

Lifecycle status. Only `active` triggers fire. Tombstoned rows are excluded from every read path.

string

Allowed values: active paused

Filter to triggers at a specific status. Values: `active | paused`.

**projectId**

string format: uuid

Only this project’s mail triggers.

## Responses

### 200

Page of mail triggers.

application/json

object

**data**

required

Array\<object>

A mail trigger: each new mail in its mailbox’s folder starts a run of its agent or flow, with the mail as the run’s input, as its owner. In such a run, every call that changes something, or uses the run’s own mailbox, waits for a person’s yes unless the trigger lists it (`toolsAllowedUnattended`).

object

**triggerId**

required

string

**flowId**

Run a flow (with `flowVersion`).

string

\>= 1 characters

**flowVersion**

string

\>= 1 characters

**agentId**

Run an agent (instead of a flow): at `agentVersion`, else its pinned version.

string

\>= 1 characters

**agentVersion**

string

\>= 1 characters

**projectId**

required

The trigger’s project: its runs are this project’s.

string format: uuid

**owner**

required

Who its runs act as, with their connections. A service account owns a shared trigger, on the company’s mailbox: one named with `runAsServiceAccountId`, or the project’s own (`runAsProject`, `isProjectAccount`). A person owns a personal one: every trigger on a person’s mailbox, and one on the company’s mailbox registered without either. No edit moves it; only a take-over (`POST …/owner`) does.

object

**kind**

required

string

Allowed values: user service

**id**

required

string

**displayName**

The owner’s name at the time of the response: the person’s display name, or the service account’s name. Absent when it can’t be read (no directory, a removed account) and from a runtime before Kindgi 0.1.6: show the id then.

string

**isProjectAccount**

Set when the owner is the trigger’s project’s own service account (`runAsProject`): the trigger is shared, and runs as the project. Show it as “This project”. Absent otherwise.

boolean

**mailbox**

required

The mailbox a mail trigger reads: its app (`google`: Gmail; `microsoft`: Outlook) and whose it is (`company`: the company’s mailbox, through the company connection of that app `connectionId` names; `person`: the trigger owner’s own, through their connection).

object

**app**

required

string

Allowed values: google microsoft

**as**

required

string

Allowed values: company person

**connectionId**

The connection it reads the mailbox with. With `as: company`, it must be one of the project’s candidates of the app (`GET /v1/connection-choices/candidates`); a person’s own live connection of the app is `400 bad-input`, and any other `404 connection-not-found`. Not named, the project’s only one is stored, so another connection added later doesn’t change what the trigger reads; with several, it’s `409 connection-not-chosen`, and with none, nothing is stored. With `as: person`, it must be the owner’s own live connection of the app, and any other is `404 connection-not-found`. Not named, their main is stored, else their only one; with several and no main, it’s `409 connection-not-chosen`, which counts them; with none, nothing is stored. A new owner clears it, and the next poll stores theirs. Another `connectionId`, or one named where there was none, is another mailbox. The mailbox it reads now, sent again with none, is the same mailbox: it keeps the one it reads with, or stores the one the rules above give. Absent from a trigger that names none.

string format: uuid

**folder**

The Gmail label id, or the Outlook folder id or well-known name, it watches. Absent: the inbox.

string

\>= 1 characters <= 512 characters

**pollSeconds**

required

How often it looks for new mail, in seconds (default 60).

integer

\>= 30 <= 3600

**runsPerHour**

required

The most runs its mail starts in an hour (default 60). Past it, a mail’s fire is `refused` (`detail` starts `over-limit:`), and that mail doesn’t run later.

integer

\>= 1 <= 600

**label**

required

string | null

**status**

required

Lifecycle status. Only `active` triggers fire. Tombstoned rows are excluded from every read path.

string

Allowed values: active paused

**statusReason**

Why it isn’t polling as it should: `connection-missing` (the mailbox’s connection is gone or lapsed; polls go on, and work again once it’s back), `connection-not-chosen` (it names no connection, and the project has several company connections of the app, which the reason names, or its owner has several and no main; it doesn’t poll until its `config.mailbox.connectionId` names one), `owner-refused` (its owner was removed or may no longer run the target; polls go on and read nothing until that changes or an admin takes it over), or why the runtime paused it.

string

**version**

The trigger’s newest version (`GET …/versions`): one more for each change to what it runs, when, as whom or with what. Absent until its first version.

integer

\>= 1

**account**

The mailbox’s account at its last poll: the connection’s account id and its label (the address). Absent before its first poll, and after a take-over of a person’s mailbox until the next poll. On a person’s mailbox (`config.mailbox.as: person`), only the trigger’s owner and a tenant admin read it; anyone else reads the trigger without it.

object

**id**

required

string

**label**

required

string

**lastPolledAt**

When it last polled the mailbox and got an answer.

string format: date-time

**toolsAllowedUnattended**

The tools whose calls in this trigger’s runs don’t wait for a person’s yes, though a run a mail started asks for one for every call that changes something or uses its mailbox. Absent: none. Each tool by its id, with no wildcard or pattern, and only tools this trigger’s flow or agent calls. It lifts only that wait: the workspace’s approval rules, the agent’s own rules, the permission check (as the trigger’s owner) and the tool’s own limits all still apply.

Array\<string>

<= 100 items unique items

**lastFiredAt**

required

string | null format: date-time

**createdAt**

required

string format: date-time

**updatedAt**

required

string format: date-time

**nextCursor**

string

**hasMore**

required

boolean

##### Example

```json
{
  "data": [
    {
      "owner": {
        "kind": "user",
        "isProjectAccount": true
      },
      "mailbox": {
        "app": "google",
        "as": "company"
      },
      "status": "active"
    }
  ]
}
```

### 401

Missing / malformed / expired / revoked bearer token.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 503

The database was too busy to answer in time (`database-busy`); `Retry-After` (whole seconds) says when to try again.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

## See also

- [`POST /v1/mail-triggers`: Register a mail trigger](https://docs.kindgi.com/reference/api/operations/mailtriggersregister.md)
- [Error code `database-busy`](https://docs.kindgi.com/reference/errors.md#database-busy)
- [Every error code](https://docs.kindgi.com/reference/errors.md)
