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

# Your first automation

> Create a webhook flow that validates an event and stores a temporary record

This example receives a generic event and stores a record for one day. It does not send messages or call an external business API.

## 1. Create a draft

Open **Automations** in your workspace, create an automation, and give it a name such as **Record an event**. Add a webhook section. You can also create the draft with `create_automation` and set its definition with `update_automation` through [MCP](https://docs.runflow.ai/automations/mcp.md).

## 2. Configure the section

Use this complete definition in an MCP update, or create the equivalent blocks in the editor:

```json theme={null}
{
  "sections": [
    {
      "id": "record_event",
      "name": "Record incoming event",
      "entry": { "kind": "webhook" },
      "match": [
        { "path": "body:event", "operator": "equals", "value": "record.created" }
      ],
      "steps": [
        {
          "id": "prepare_step",
          "key": "prepare",
          "name": "Validate and prepare",
          "action": {
            "type": "transform",
            "code": "function transform(input) { const b = input.body; if (!b || typeof b.id !== 'string' || !b.id.trim()) throw new Error('id is required'); return { id: b.id.trim(), event: b.event, receivedAt: new Date().toISOString() }; }"
          },
          "onError": "stop"
        },
        {
          "id": "save_step",
          "key": "save",
          "name": "Store for one day",
          "action": {
            "type": "kv.set",
            "namespace": "event-records",
            "key": "{{steps:prepare.id}}",
            "ttlSeconds": 86400
          },
          "input": { "value": "steps:prepare" },
          "onError": "stop"
        }
      ]
    }
  ]
}
```

The first step creates `steps:prepare`. The second maps that whole object into `input.value`. `namespace` is fixed; `key` is rendered from the prepared event ID.

## 3. Test before activation

Select `record_event` and use this sample payload:

```json theme={null}
{ "event": "record.created", "id": "example-001" }
```

Check that the entry conditions match, `prepare` returns the expected object, and `save` reports a **simulated** write. The test does not persist this KV value. Also test a missing ID: the transform should fail and the write should not execute.

> [!WARNING]
>
> Test mode is not a universal offline mode. This example only transforms data and simulates a write. Other blocks can perform real reads or model calls. Review the [test-mode matrix](https://docs.runflow.ai/automations/testing-and-operations.md#what-test-mode-executes) before testing a different flow.

## 4. Activate and send one controlled event

After reviewing the definition, activate it and copy its webhook URL from the Portal. Keep this URL private: its token grants access to that entry point.

```bash theme={null}
# Set AUTOMATION_WEBHOOK_URL to the URL shown by the Portal.
curl --request POST "$AUTOMATION_WEBHOOK_URL" \
  --header 'Content-Type: application/json' \
  --header 'X-Idempotency-Key: example-record-001' \
  --data '{"event":"record.created","id":"example-001"}'
```

Admission returns HTTP `202` for an accepted request. That means the request was admitted, not that all steps have finished. Open the run history, inspect both steps, and verify the KV record through the workspace's data tools.

Reusing an idempotency key refers to the same logical request during its deduplication window. Generate a new key for a genuinely new operation, not merely because a response timed out.

## 5. Extend the flow

* Add an **If** block to branch on a record's attributes.
* Add **Connector** or **HTTP** to update another system.
* Add **Send message** when the destination, connection, template, and messaging eligibility have been validated.
* Bind an existing trigger if callers must keep their URL. Start with a narrow event condition; see [routing](https://docs.runflow.ai/automations/data-and-routing.md#keep-an-existing-trigger-url).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.