## `POST /sop/:name/:id/steps/:step_id/submit`

**Submit a step** — Advances a step by providing its outputs. The step must be in the `active` state. Submitting a terminal step or a step in the wrong state returns `422 invalid_transition`.

> **Auth:** Requires `X-SOP-Token` header. See [Authentication](/api-docs/guides/authentication.md).

### Path parameters

- `name` (string, required) — The process name.
- `id` (string, required) — The instance ULID.
- `step_id` (string, required) — The step `id` as declared in the process YAML.

### Request body

**Content-Type:** `application/json`

- `outputs` (object, required) — Key-value map of step outputs as declared in the process YAML.

### Response

`200 application/json` — Updated step object.

```json
{
  "id":      "review-application",
  "state":   "completed",
  "outputs": { "decision": "approved" }
}
```

### Errors

| Status | Code | Meaning |
|--------|------|---------|
| 422 | `invalid_transition` | Step is not in the active state. |
| 404 | `not_found` | Instance or step not found. |

### Code examples

#### curl

```bash
curl https://api.opensop.dev/sop/customer-onboarding/01HXYZ_ACME_001/steps/review-application/submit \
  -X POST \
  -H "X-SOP-Token: $OPENSOP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"outputs":{"decision":"approved","reason":"Documents verified"}}'
```

#### Node

```js
await fetch(
  `https://api.opensop.dev/sop/customer-onboarding/${id}/steps/review-application/submit`,
  {
    method: "POST",
    headers: {
      "X-SOP-Token": process.env.OPENSOP_TOKEN,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ outputs: { decision: "approved" } })
  }
);
```

#### Python

```python
requests.post(
  f"https://api.opensop.dev/sop/customer-onboarding/{id}/steps/review-application/submit",
  json={"outputs": {"decision": "approved"}},
  headers={"X-SOP-Token": os.environ["OPENSOP_TOKEN"]}
)
```

#### Ruby

```ruby
req = Net::HTTP::Post.new("/sop/customer-onboarding/#{id}/steps/review-application/submit")
req["X-SOP-Token"]   = ENV["OPENSOP_TOKEN"]
req["Content-Type"] = "application/json"
req.body = { outputs: { decision: "approved" } }.to_json
Net::HTTP.start("api.opensop.dev", use_ssl: true) { |h| h.request(req) }
```

