---
title: "Trigger a project's declared action"
method: POST
path: "/v1/projects/{projectId}/actions/{key}/trigger"
tags: ["Loops", "Projects"]
---

# Trigger a project's declared action

`POST /v1/projects/{projectId}/actions/{key}/trigger`

## Request

- Base URL: `https://api.tessl.io`
- URL: `https://api.tessl.io/v1/projects/{projectId}/actions/{key}/trigger`
- Auth: none declared

Runs a declared action as if an event trigger had matched it. Answers 202 once the request and its claim are recorded, before the run is admitted; what happens next is shown in the Loop history. A request that names a `requestId` already recorded for the action records nothing and starts no second run: it answers 202 with the first request's id and `alreadyRecorded` true, after the same checks as a first request. Answers 409 when the action's Loop is paused, and records nothing. Answers 404, and records nothing, while the `factory` feature is off for the project's organization.

## Path parameters

- `projectId` string, uuid, required
- `key` string, required

## Headers

- `Authorization` string

## Request body

- object
  - `requestId` unknown
  - `action` object: Overrides of the declared action, by the rule a manifest's `use` override follows: a value replaces the declared one, `null` removes it, and `inputs` merges name by name.
    - `skill` unknown
    - `environment` unknown
    - `instructions` unknown
    - `agent` unknown
    - `model` unknown
    - `effort` unknown
    - `workdir` unknown
    - `baseBranch` unknown
    - `snapshotFamily` unknown
    - `sandboxSnapshot` unknown
    - `inputs` unknown
    - `timeout` unknown
  - `links` unknown
  - `concurrency` object: The trigger's concurrency: `keys`, each a plain string or `{ key, limit? }`, held beside the project's with `limit` 1 for a string or an object that names none, and `mode` (`join`, `queue` or `skip`), `join` when it names none.
    - `keys` unknown
    - `mode` unknown

## Response `202`

Default Response

- object
  - `links` object, required
    - `self` string, uri, required
  - `data` object, required
    - `id` string, required: The id of the recorded request.
    - `type` 'action-trigger', required
    - `attributes` object, required
      - `loopId` string, uuid, required
      - `key` string, required
      - `alreadyRecorded` boolean, required: Whether an earlier request with the same `requestId` recorded this one, so this call recorded nothing.

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Default Response
- `404`: Not Found
- `409`: Conflict
- `500`: Internal Server Error

## Changes

- **2026-10-21** `4f3ca2dfb0bc`: 1 info
  - added the new optional request property `links`
- **2026-10-19** `27e82363fbbf`: 2 info
  - added the new optional request property `requestId`
  - added the required property `data/attributes/alreadyRecorded` to the response with the `202` status
- **2026-10-15** `ab89e2ebb5dd`: 1 info
  - added the non-success response with the status `409`
- **2026-10-15** `9bc40fce2fb7`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/tessl/apis/tessl-api/changes/v1/projects/:projectId/actions/:key/trigger/post.md)

---

[API](https://skmtc.dev/tessl/apis/tessl-api.md) · [All operations](https://skmtc.dev/tessl/apis/tessl-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tessl/apis/tessl-api/revisions/7386f1e6295b?raw)
