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

# Trigger a project's declared action

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

## Request

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

Runs a declared action as if an event trigger had matched it. When the project's `tessl.json` declares no action with the name, runs the project's Loop declared in the app whose key it is, with that Loop's stored action. 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
- `name` 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
  - `labelFragments` 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
      - `name` string, required: The action's name in the project's manifest, or the key of the Loop declared in the app.
      - `key` string, required: The same value as `name`, kept for clients that read it.
      - `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-29** `0d008f56f0c1`: 1 info
  - added the new optional request property `labelFragments`
- **2026-10-29** `06d2142ed144`: 1 info
  - api operation id `triggerProjectAction` was added

[Operation history](https://skmtc.dev/tessl/apis/tessl-api/changes/v1/projects/:projectId/actions/:name/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/4fa2a17154e4?raw)
