# Get a field’s sub-fields

**POST** `/api/pieces/{id}/actions/{actionId}/properties/{propertyName}`

Asks an app for the fields of a field whose shape depends on other values, such as a sub-flow’s inputs.

Needs [an API token](https://docs.gorunner.io/api/authentication#scopes) with the **Read** permission for workflows (`workflow:read`).

## Parameters

### Path

- `id` · Required · string: The app’s id.
- `actionId` · Required · string: The action the field belongs to.
- `propertyName` · Required · string: The field to describe.

### Body

Send at least `{}`: no body at all is a 400.

- `values` · Optional · object: The step’s settings so far, as in its `config`. For a sub-flow, the workflow to call in `workflow_id`.
- `connectionId` · Optional · UUID: A connection the app may sign in with. A worker reads it; the API never does. Sending one also needs the **Write** permission for workflows and the **Read** permission for connections.

## The sub-flow case

The Sub-flow step’s `input` is answered by the API itself: the inputs the called workflow expects, filled from its trigger’s sample. Without `workflow_id` you get a placeholder.

Other apps are asked on a worker, which may sign in to the service with the connection. That’s why it needs the **Read** permission for workflows: it can return your workspace’s data. See [Run another workflow](https://docs.gorunner.io/build/run-another-workflow).

## Responses

- 200 OK

  The fields in `properties`, by name, in the same shape as an action’s own.

- 400 `INVALID_BODY`

  The body is missing or isn’t JSON.

- 400 `NOT_SUPPORTED`

  This app has no fields of this kind. Most don’t.

- 403 `FORBIDDEN`

  The token lacks the **Read** permission for workflows. With `connectionId`, it also needs the **Write** permission for workflows and the **Read** permission for connections, because the call uses that connection’s sign-in. `details.permission` names the one missing.

- 403 `PIECE_NOT_ALLOWED`

  This workspace may not use the app.

- 404 `NOT_FOUND`

  No worker has this app.

- 500 `RESOLVE_FAILED`

  The sub-flow’s inputs couldn’t be read.

- 500 `INTERNAL_ERROR`

  The worker failed or answered something unreadable.

- 503 `WORKER_UNAVAILABLE`

  No worker is checked in, so the app couldn’t be asked. Workflows are unaffected.

- 503 `PIECES_UNAVAILABLE`

  GoRunner couldn’t check which apps this workspace may use. Try again.

Any call can also answer: [`401 UNAUTHORIZED`](https://docs.gorunner.io/errors#UNAUTHORIZED) · [`401 TOKEN_ENDED`](https://docs.gorunner.io/errors#TOKEN_ENDED) · [`403 TOKEN_SURFACE_OFF`](https://docs.gorunner.io/errors#TOKEN_SURFACE_OFF) · [`403 FEATURE_OFF`](https://docs.gorunner.io/errors#FEATURE_OFF) · [`403 EMBED_NOT_ALLOWED`](https://docs.gorunner.io/errors#EMBED_NOT_ALLOWED) · [`403 EMBED_NOT_AVAILABLE`](https://docs.gorunner.io/errors#EMBED_NOT_AVAILABLE) · [`403 ACCOUNT_SUSPENDED`](https://docs.gorunner.io/errors#ACCOUNT_SUSPENDED) · [`413 PAYLOAD_TOO_LARGE`](https://docs.gorunner.io/errors#PAYLOAD_TOO_LARGE) · [`415 INVALID_CONTENT_TYPE`](https://docs.gorunner.io/errors#INVALID_CONTENT_TYPE) · [`429 RATE_LIMITED`](https://docs.gorunner.io/errors#RATE_LIMITED) · [`503 BUSY`](https://docs.gorunner.io/errors#BUSY) · [`503 PAUSED`](https://docs.gorunner.io/errors#PAUSED) · [`503 SERVICE_UNAVAILABLE`](https://docs.gorunner.io/errors#SERVICE_UNAVAILABLE). See [Errors](https://docs.gorunner.io/api/errors#by-status).

Request

**curl**

```bash
GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…"  # needs Read for workflows
APP_ID="subflow"
ACTION_ID="execute"
FIELD="input"

URL="$GORUNNER/api/pieces/$APP_ID/actions/"
URL="${URL}$ACTION_ID/properties/$FIELD"
curl -X POST "$URL" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "values": {
      "workflow_id": "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31"
    }
  }'
```

**JavaScript**

```js
const GORUNNER = "https://api.gorunner.io";
const TOKEN = process.env.GORUNNER_TOKEN;
const APP_ID = process.env.APP_ID;
const ACTION_ID = process.env.ACTION_ID;
const FIELD = process.env.FIELD;

const res = await fetch(
  `${GORUNNER}/api/pieces/${APP_ID}/actions/` +
    `${ACTION_ID}/properties/${FIELD}`,
  {
    method: "POST",
    headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
    body: JSON.stringify({
        values: {
          workflow_id: "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
        },
      }),
  },
);
const { data } = await res.json();
```

**Python**

```python
import os, requests

GORUNNER = "https://api.gorunner.io"
TOKEN = os.environ["GORUNNER_TOKEN"]
APP_ID = os.environ["APP_ID"]
ACTION_ID = os.environ["ACTION_ID"]
FIELD = os.environ["FIELD"]

res = requests.post(
    f"{GORUNNER}/api/pieces/{APP_ID}/actions/"
    f"{ACTION_ID}/properties/{FIELD}",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
      "values": {
        "workflow_id": "3b1e5c2a-7d4f-4e8b-a1c6-5f2d9b0e7a31",
      },
    },
)
data = res.json()["data"]
```

Response · 200 OK

```json
{
  "data": {
    "properties": {
      "input": {
        "type": "object",
        "title": "Input",
        "description": "\"Order intake\" declares the input it expects; this field is pre-filled from that declaration.",
        "propertyType": "JSON",
        "order": 2,
        "default": {
          "orderId": ""
        }
      }
    }
  }
}
```

**Every field**

The step configuration so far, plus the connection to resolve credentials with. Send at least `{}`: no body at all is a 400 `INVALID_BODY`.

### Body

- `values` · Optional · object: The step’s configuration so far, keyed by property name — the same object that becomes the step’s `config`. For the sub-flow case put the target workflow’s UUID in `values.workflow_id`.
- `connectionId` · Optional · UUID: Connection whose credentials the app should use. Only the id travels; the worker decrypts it.

### Response · 200 OK

- `data` · object: The resolved sub-schema.
  - `properties` · object: Resolved properties keyed by property name, in the full (unprojected) property shape.
    - `{key}` · object: One property of a full (unprojected) schema. Recursive through `properties` and `items`. Carries presentation fields (`title`, `order`, `placeholder`, `group`) that the projected views drop. Each key’s value.
      - `type` · string: JSON type: string, number, boolean, object, array.
      - `title` · string
      - `description` · string: The field’s help as the step panel shows it.
      - `default`: Default value, of any JSON type.
      - `enum` · array of strings: Closed set of legal values.
      - `enumLabels` · object: Display labels for the enum values.
        - `{key}` · string: Each key’s value.
      - `format` · string
      - `properties` · object
        - `{key}` · object: One property of a full (unprojected) schema. Recursive through `properties` and `items`. Carries presentation fields (`title`, `order`, `placeholder`, `group`) that the projected views drop. The same fields as `data.properties.{key}` above.
      - `items` · object: One property of a full (unprojected) schema. Recursive through `properties` and `items`. Carries presentation fields (`title`, `order`, `placeholder`, `group`) that the projected views drop. The same fields as `data.properties.{key}` above.
      - `propertyType` · string: How the value is chosen: SHORT_TEXT, LONG_TEXT, STATIC_DROPDOWN, DROPDOWN, MULTI_SELECT, DYNAMIC, JSON, CODE, CHECKBOX, NUMBER, SECRET_TEXT and similar. DROPDOWN / MULTI_SELECT / DYNAMIC mean the legal values come from POST.../options/{propertyName} at run time, not from this schema.
      - `placeholder` · string
      - `group` · string
      - `order` · integer
      - `required` · boolean
      - `minLength` · integer
      - `maxLength` · integer
      - `min` · number
      - `max` · number
      - `pattern` · string
      - `refreshers` · array of strings: Other property names whose change invalidates this property’s resolved options.
      - `refreshOnSearch` · boolean
      - `expressionEnabled` · boolean
      - `language` · string: For CODE properties: the language of the editor, e.g. javascript.
      - `displayConditions` · array of objects: ANDed conditions deciding whether this property applies. The fields they name are this operation’s gate fields — the ones you can pass as query parameters to narrow the operation detail.
        - `field` · string: The sibling property whose value decides this one. Every gate field is itself a property of the same schema, so its declared type and enum are the legal values.
        - `operator` · string: An unrecognized operator is treated permissively (the property is kept). One of `eq`, `neq`, `in`, `notIn`, `contains_any`, `exists`, `notExists`.
        - `value`: Comparison value for eq/neq, of any scalar JSON type.
        - `values` · array: Candidate list for in / notIn / contains_any.
      - `bound` · boolean: The value, though typed as text, points into the connected account (a spreadsheet, a calendar, a folder, a label): it means nothing to another account, so a template used outside the workspace it was saved in arrives with it blank. Absent when false.
      - `noExpressions` · boolean: True when `{{ }}` is NOT replaced in this field: the value is used exactly as written (Code 2.0.0’s `code`, where values come from `inputs`, `steps` and `trigger` instead). Never write an expression into such a field.
      - `agentNotes` · string: What an agent writing this field must know beyond `description`; the step panel does not show it. The projected views append it to `description`. Absent when there is none.

Related: [Get a field’s options](https://docs.gorunner.io/api/apps/resolve-dynamic-options) · [Get an action’s fields](https://docs.gorunner.io/api/apps/get-app-action)

Source: https://docs.gorunner.io/api/apps/resolve-dynamic-properties
