> ## Documentation Index
> Fetch the complete documentation index at: https://docs.membrane.agency/llms.txt
> Use this file to discover all available pages before exploring further.

# Act

> Run an operation against a connected app through a connection. Dispatch inline — `api` for a raw HTTP request (sent through the connection’s auth + base URL) or `code` for sandboxed JS — or run a saved action by `id`/`key`. Pick the connection with `connectionId`/`connectionKey` (or `integrationId`/`integrationKey`). Returns `{ output, actionRunId }`; reads real data from the app, never mock it.

`POST /act` is Membrane's unified entry point for running an action through a connection. See the [Actions guide](/docs/how-membrane-works/actions) for the conceptual walkthrough — this page is the endpoint reference.

## Dispatch styles

Exactly **one** of `id`, `key`, `api`, or `code` per call. Sending more than one returns `400`.

| Field  | Style         | When to use                                                                             |
| ------ | ------------- | --------------------------------------------------------------------------------------- |
| `api`  | Inline `api`  | Make an ad-hoc HTTP request through the connection's auth and base URL.                 |
| `code` | Inline `code` | Run a JavaScript snippet in a sandbox with a pre-wired authenticated `membrane` client. |
| `key`  | Reusable      | Run a saved (workspace- or integration-level) action by its stable key.                 |
| `id`   | Reusable      | Run a catalog action or a saved action by its Membrane ID.                              |

## Resolving a connection

Every dispatch needs a connection. Membrane resolves one in this order:

1. `connectionId` — direct.
2. `connectionKey` — look up by key.
3. `integrationId` or `integrationKey` — use that integration's default connection.
4. Workspace default connection.

Reusable-action dispatches that target a connection-scoped or integration-scoped action use the action's own scope automatically — the body fields above only kick in when the action's scope doesn't already resolve a connection.

For inline dispatches (`api` / `code`), the connection must resolve to something — otherwise the request returns `400`.

## Request body

<RequestExample>
  ```json api theme={null}
  {
    "connectionKey": "hubspot-prod",
    "api": {
      "method": "POST",
      "path": "/crm/v3/objects/contacts",
      "body": { "properties": { "email": "jane@example.com" } }
    }
  }
  ```

  ```json code theme={null}
  {
    "connectionKey": "hubspot-prod",
    "code": "module.exports = async ({ input, membrane }) => ({ ok: true })",
    "input": {}
  }
  ```

  ```json key theme={null}
  {
    "key": "create-contact",
    "integrationKey": "hubspot",
    "connectionKey": "hubspot-prod",
    "input": { "email": "jane@example.com" }
  }
  ```

  ```json id theme={null}
  {
    "id": "action_abc",
    "connectionKey": "hubspot-prod",
    "input": { "email": "jane@example.com" },
    "meta": { "source": "support-ticket-1234" }
  }
  ```
</RequestExample>

| Field            | Type   | Description                                                                                                 |
| ---------------- | ------ | ----------------------------------------------------------------------------------------------------------- |
| `api`            | object | Inline HTTP spec — `{ method, path, body?, headers?, query? }`.                                             |
| `code`           | string | Inline JS module body, executed in a sandbox.                                                               |
| `key`            | string | Action key. Scope with `integrationKey`/`integrationId`/`connectionId` if non-unique.                       |
| `id`             | string | Action ID. Mutually exclusive with `key` / `api` / `code`.                                                  |
| `connectionId`   | string | Connection to run against. See [resolution priority](#resolving-a-connection).                              |
| `connectionKey`  | string | Connection key alternative to `connectionId`.                                                               |
| `integrationId`  | string | Integration ID — used to scope `key` lookups or pick a default connection.                                  |
| `integrationKey` | string | Integration key alternative to `integrationId`.                                                             |
| `input`          | object | Action input. For reusable-action dispatches, validated against the action's `inputSchema`.                 |
| `meta`           | object | Arbitrary metadata stored on the resulting [Action Run Log](/reference/workspace-elements/action-run-logs). |

## Response

```json theme={null}
{
  "output": { "id": "12345", "email": "jane@example.com" },
  "actionRunId": "<action-run-id>"
}
```

Every response — success or failure — carries an `actionRunId`. Pass it to [`GET /action-run-logs/{id}`](/reference/workspace-elements/action-run-logs) to inspect the resolved action, mapped input, raw HTTP exchange, and any errors. On failure the body still includes `actionRunId` alongside the error.

## Replay from a run log

Every action run log carries an `action` field — the complete `/act` body that was used. Pass it back unchanged to re-run the same dispatch:

```bash theme={null}
SNAPSHOT=$(curl -s https://api.integration.app/action-run-logs/<runId> \
  -H "Authorization: Bearer $TOKEN" | jq '.action')

curl -X POST https://api.integration.app/act \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "$SNAPSHOT"
```

The snapshot is the source of truth for "what ran". Top-level `input` and `connectionId` on the run log are deprecated — read them from `action` instead.

## Legacy: `POST /actions/{selector}/run`

`POST /actions/{selector}/run` is the older endpoint for running a saved action. It still works for existing callers but is **deprecated** — `/act` covers all the same cases plus inline `api` and `code` dispatch.

| Before                                           | After                                                                                     |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| `POST /actions/<id>/run` body: `{ ...input }`    | `POST /act` body: `{ "id": "<id>", "input": {...}, "connectionKey": "..." }`              |
| `POST /actions/<key>/run?connectionId=<id>`      | `POST /act` body: `{ "key": "<key>", "connectionId": "<id>", "input": {...} }`            |
| Ad-hoc HTTP via a custom `run-javascript` action | `POST /act` body: `{ "api": { "method": "...", "path": "..." }, "connectionKey": "..." }` |

Use `/act` for new code.


## OpenAPI

````yaml POST /act
openapi: 3.0.0
info:
  title: Membrane API
  description: API documentation for Membrane Engine
  version: '1.0'
  contact: {}
servers:
  - url: https://api.getmembrane.com
security:
  - bearer: []
tags: []
paths:
  /act:
    post:
      summary: act
      description: >-
        Run an operation against a connected app through a connection. Dispatch
        inline — `api` for a raw HTTP request (sent through the connection’s
        auth + base URL) or `code` for sandboxed JS — or run a saved action by
        `id`/`key`. Pick the connection with `connectionId`/`connectionKey` (or
        `integrationId`/`integrationKey`). Returns `{ output, actionRunId }`;
        reads real data from the app, never mock it.
      operationId: act
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  description: ID of the action to run.
                  type: string
                key:
                  description: >-
                    Key of the action to run. If the same key exists across
                    multiple integrations, also pass `integrationId`,
                    `integrationKey`, `connectionId`, or `connectionKey` so we
                    know which one you mean.
                  type: string
                api:
                  description: >-
                    Inline HTTP request spec, sent through the resolved
                    connection's auth layer and base URL. Pair with
                    `connectionId` or `connectionKey` to pick the connection.
                  type: object
                  properties:
                    method:
                      type: string
                      enum:
                        - GET
                        - POST
                        - PATCH
                        - PUT
                        - DELETE
                      description: HTTP method to use.
                    path:
                      description: >-
                        Path appended to the connection's base URL, e.g.
                        `/v2/users/me`. Required.
                      type: string
                    body:
                      description: Request body. For JSON APIs, pass a plain object.
                    headers:
                      description: Additional HTTP headers.
                      type: object
                      additionalProperties:
                        type: string
                    query:
                      description: Query-string parameters.
                      type: object
                      additionalProperties:
                        anyOf:
                          - type: string
                          - type: number
                          - type: boolean
                    url:
                      description: >-
                        [INTERNAL] Alias for `path`. Normalized to `path` on the
                        server.
                      type: string
                    data:
                      description: >-
                        [INTERNAL] Alias for `body`. Normalized to `body` on the
                        server.
                  required:
                    - method
                  additionalProperties: false
                code:
                  description: >-
                    Inline JavaScript executed in a sandbox. The module must
                    export a function that receives `{ input, membrane, ... }`.
                    Its return value becomes the action output.
                  type: string
                integrationId:
                  description: >-
                    ID of the integration that owns the action element. When
                    `key` is provided, looks up the action within this
                    integration. If the action needs a connection, also pass
                    `connectionId` or `connectionKey`.
                  type: string
                integrationKey:
                  description: >-
                    Key of the integration that owns the action element. When
                    `key` is provided, looks up the action within this
                    integration. If the action needs a connection, also pass
                    `connectionId` or `connectionKey`.
                  type: string
                connectionId:
                  description: >-
                    ID of the connection to run the action against. If a
                    connection-level version of the action exists on this
                    connection, that version is used; otherwise the
                    integration-level or universal action is used.
                  type: string
                connectionKey:
                  description: >-
                    Key of the connection to run the action against. Use when
                    you have a stable key for the connection instead of an ID.
                  type: string
                input:
                  description: >-
                    Input passed to the action, matching its input schema. Must
                    be a JSON object.
                  type: object
                  additionalProperties: {}
                meta:
                  description: Arbitrary metadata stored on the action run log.
                  type: object
                  additionalProperties: {}
              additionalProperties: false
      responses:
        '200':
          description: >-
            Run an operation against a connected app through a connection.
            Dispatch inline — `api` for a raw HTTP request (sent through the
            connection’s auth + base URL) or `code` for sandboxed JS — or run a
            saved action by `id`/`key`. Pick the connection with
            `connectionId`/`connectionKey` (or
            `integrationId`/`integrationKey`). Returns `{ output, actionRunId
            }`; reads real data from the app, never mock it.
          content:
            application/json:
              schema:
                type: object
                properties:
                  output: {}
                  actionRunId:
                    type: string
                additionalProperties: false
components:
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: JWT
      type: http

````