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

# Workflows — integrations & interpolation

> The webhook and email actions, secure credentials, date offsets, and the interpolation template syntax.

Beyond plan actions, workflows can call external systems and send email — and any field
can reference earlier values through interpolation.

## CALL\_WEBHOOK

```json theme={null}
{
  "actionType": "CALL_WEBHOOK",
  "url": "https://example.com/hook",
  "method": "POST",
  "headers": [ { "key": "X-Source", "value": "solya" } ],
  "queryParams": [ { "key": "ref", "value": "{{workflow.id}}" } ],
  "bodyType": "json",
  "bodyTemplate": "{ \"plan\": \"{{steps.create.output.planId}}\" }",
  "auth": { "type": "bearer", "credentialId": "<uuid>" },
  "timeoutMs": 10000,
  "retries": { "count": 2, "backoffMs": 1000 },
  "successStatusCodes": [200, 201, [200, 299]],
  "saveResponseAs": "hookResult"
}
```

| Field                                           | Meaning                                                                                                                 |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `method`                                        | `GET` · `POST` · `PUT` · `PATCH` · `DELETE`.                                                                            |
| `url`, `headers`, `queryParams`, `bodyTemplate` | All support [interpolation](#interpolation).                                                                            |
| `bodyType`                                      | `none` · `json` · `form` · `raw` (`bodyContentType` for `raw`).                                                         |
| `auth`                                          | `{ type: "none" }`, or `bearer` / `basic` / `apiKey` referencing a `credentialId` (`apiKey` also takes a `headerName`). |
| `timeoutMs` / `retries`                         | Execution policy (`retries`: `{ count, backoffMs }`).                                                                   |
| `successStatusCodes`                            | Codes (or `[from, to]` ranges) treated as success.                                                                      |
| `saveResponseAs`                                | Names the response output so it's reachable downstream.                                                                 |

## SEND\_EMAIL

```json theme={null}
{
  "actionType": "SEND_EMAIL",
  "to": ["buyer@example.com"],
  "cc": [], "bcc": [], "replyTo": "ops@solya.app",
  "fromAddress": "no-reply@solya.app",
  "fromName": "Solya",
  "subject": "Restock plan {{steps.create.output.planName}} ready",
  "bodyFormat": "markdown",
  "bodyTemplate": "Added **{{steps.add.output.addedCount}}** items.",
  "tags": ["restock"],
  "trackOpens": true,
  "trackClicks": false
}
```

Sent via Mailgun from a verified sender domain. `bodyFormat` is `text`, `html`, or
`markdown`. **Output:** `messageId`.

## Credentials

Webhook secrets never live in the flow definition — they're stored separately and
referenced by `credentialId`.

* Types: `BEARER_TOKEN`, `BASIC_AUTH`, `API_KEY`.
* Stored **encrypted at rest** (AES-256-GCM); the flow only holds the credential's id.
* The app exposes a **safe view** (id, name, type — never the secret); the secret is
  resolved server-side only when the data platform executes the call.

## Date offsets

Plan dates (`deadline`, `startDate`, `endDate`) use a `DateOffset`:

| Kind           | Shape                                                               | Meaning                   |
| -------------- | ------------------------------------------------------------------- | ------------------------- |
| `absolute`     | `{ "kind": "absolute", "iso": "2026-09-01T00:00:00Z" }`             | A fixed ISO date-time.    |
| `relative`     | `{ "kind": "relative", "offsetDays": 30 }`                          | N days from `{{now}}`.    |
| `interpolated` | `{ "kind": "interpolated", "template": "{{steps.x.output.date}}" }` | Resolved from a template. |

## Interpolation

Any interpolated field uses `{{ … }}` with dot-notation. Resolution happens at run time on
the data platform (the builder offers autocomplete).

**Static variables**

| Variable                                          | Value                                          |
| ------------------------------------------------- | ---------------------------------------------- |
| `{{trigger.entityType}}` / `{{trigger.entityId}}` | The triggering entity kind / first matched id. |
| `{{org.id}}` / `{{org.name}}`                     | The organization.                              |
| `{{workflow.id}}` / `{{workflow.name}}`           | The workflow.                                  |
| `{{now}}`                                         | ISO timestamp at execution start.              |

**Step outputs** — `{{steps.<nodeId>.output.<field>}}`, e.g.:

* create-plan: `planId`, `planType`, `planName`, `isNew`
* add-items: `addedCount`, `skippedCount`
* webhook: your `saveResponseAs` field
* email: `messageId`

<Note>
  Output names line up with the step records — see
  [Runs & examples](/en/developers/workflows/runs-and-examples).
</Note>
