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

# Starting a workflow from your own service

> Send data into HERO from anywhere and start an automation with it — how to authenticate, what to post, and how a workflow reads the fields you sent.

An automation is a workflow in a HERO project: something happens, and HERO does
something in response. Most start from a change in your workspace — a row is
added, a document is signed. This page covers the other way in: **your service
posts data, and a workflow runs with it.**

Any automation can be started this way. There is no trigger to add and nothing to
switch on beyond the credential you use.

## Two ways in

<CardGroup cols={2}>
  <Card title="Webhook" icon="webhook">
    For another service calling HERO. Authenticated by a secret that belongs to
    one automation, so the caller needs no HERO account.
  </Card>

  <Card title="API" icon="code">
    For your own code. Authenticated by your personal token, so the run records
    who started it.
  </Card>
</CardGroup>

### Webhook

```bash theme={null}
curl -X POST https://app.myhero.so/hooks/automations/aut_9f3k2m8xq1p7w4vz6nrb \
  -H "X-Hero-Webhook-Secret: whsec_…" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@example.com","plan":"pro"}'
```

Find the URL and generate the secret in the automation's **Trigger reference**
panel. The secret is shown once — only a hash is stored, so it cannot be
recovered later, but it can be rotated at any time.

Generating that secret is what allows webhook calls. Rotating it invalidates
every caller using the old one; there is no separate switch to turn off.

<Note>
  The secret may be sent as `X-Hero-Webhook-Secret` or as
  `Authorization: Bearer whsec_…`, whichever your service can set.
</Note>

### API

```bash theme={null}
curl -X POST https://app.myhero.so/v1/automations/aut_9f3k2m8xq1p7w4vz6nrb/run \
  -H "Authorization: Bearer hero_ak_…" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@example.com","plan":"pro"}'
```

The API runs the automation **as its owner**, not as you — so it can do whatever
the owner could, with the owner's AI credit. Because of that it takes **edit
access** to the automation's project: a token that can only read it is refused
with `403`.

## What you can post

Any JSON body, up to **1 MB**. An object or an array both work — a service that
batches events into a list is a normal caller, not a special case.

A body larger than the limit is refused with `413` rather than truncated, so a
run never acts on half a message.

<Note>
  Both surfaces accept **200 requests per minute**. Above that you get `429`.
</Note>

## Reading the fields you sent

Anywhere a step takes text — an email body, an AI instruction, a cell value —
write a `hero:trigger/payload` token and HERO substitutes what you posted.

Put a **dot** after `payload`, then use **slashes** to go deeper:

| You posted                     | Token                                 | Resolves to       |
| ------------------------------ | ------------------------------------- | ----------------- |
| `{"email":"ada@example.com"}`  | `hero:trigger/payload.email`          | `ada@example.com` |
| `{"user":{"name":"Ada"}}`      | `hero:trigger/payload.user/name`      | `Ada`             |
| `{"answers":[{"text":"Yes"}]}` | `hero:trigger/payload.answers/0/text` | `Yes`             |
| `[{"id":"evt_1"}]`             | `hero:trigger/payload.0/id`           | `evt_1`           |

The `@` menu offers **what you posted** as a starting point and inserts
`@[what you posted](hero:trigger/payload)`, which resolves to the whole body.
Add the path yourself to narrow it to one field — the label is only what you
read, the part in brackets is what resolves.

<Warning>
  Only the first separator is a dot. `hero:trigger/payload.user.name` is not a
  path and resolves to nothing — use `hero:trigger/payload.user/name`.
</Warning>

A field you did not send resolves to an empty string, not to the token itself. A
step that depends on one will act on a blank rather than fail loudly, so branch
on it if it matters:

```json theme={null}
{ "kind": "value", "conjunction": "and",
  "predicates": [
    { "left": "@[Plan](hero:trigger/payload.plan)", "op": "is", "right": "pro" }
  ] }
```

## Routing one endpoint to different work

Services that send several kinds of event usually post a type field. Start one
automation, then split on that field with a **path** step — one branch per event
kind, and a fallback for everything you have not handled yet.

That keeps one URL and one secret per integration, rather than an automation for
every message type your service might send.

## Checking what arrived

Open the automation and expand a run. A run started by a post shows **What was
posted** — the exact body HERO received.

Reach for it first when a step produces nothing: almost always the field is
nested a level deeper than the token assumed, or the service names it something
else. The run cannot tell you that a field was missing, because a missing field
and an empty one resolve the same way.

## Responses

| Code  | Meaning                                                                                                                                             |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `202` | Queued. The body carries `runId` — poll the run for its outcome.                                                                                    |
| `401` | Unknown automation, or the wrong secret. Deliberately the same answer for both, so the endpoint cannot be used to discover which automations exist. |
| `403` | API only: the token can read the project but not edit it.                                                                                           |
| `409` | The automation is turned off, or already has a run pending.                                                                                         |
| `413` | Body over 1 MB.                                                                                                                                     |
| `429` | Over 200 requests per minute.                                                                                                                       |

### Not sending the same thing twice

Send an `Idempotency-Key` header — on either endpoint — and a repeat of the same
key returns the original run instead of starting another:

```bash theme={null}
curl -X POST https://app.myhero.so/hooks/automations/aut_… \
  -H "X-Hero-Webhook-Secret: whsec_…" \
  -H "Idempotency-Key: evt_01HZY3" \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@example.com"}'
```

The response is `200` with `replayed: true`, so a service that retries on timeout
cannot run your workflow twice. Use the sending service's own event id.
