# Workflow: Build an Editor Workflow

## Purpose

Add a manual action to the Riverbank dashboard editor. Editor workflows are declared in `riverbank.config.ts` and run against either a content type or the page editor.

## Configure

Add a workflow to SDK config, then run `riverbankcms push-config`.

```ts
export default {
  siteId: process.env.RIVERBANK_SITE_ID,
  previewUrl: "https://preview.example.com",
  workflows: [
    {
      id: "page.generate-seo",
      title: "Generate SEO metadata",
      description: "Draft page SEO fields from the current page.",
      contentType: "page",
      endpoint: "/api/workflows/generate-seo",
      triggers: [{ type: "manual" }],
    },
  ],
};
```

Use `contentType: "page"` for page workflows. Use a content type key, such as `blog-post`, for entry workflows. If `workflows` is missing or empty, the dashboard does not show the workflow section.

## Handle the Request

Riverbank saves the draft, disables the editor, creates a workflow run, and posts to your endpoint. Verify the raw body with the same HMAC helper used for webhooks:

```ts
import { verifyWebhookSignature } from "@riverbankcms/sdk/webhooks";

export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get("x-riverbank-signature");
  const secret = process.env.RIVERBANK_WEBHOOK_SECRET;

  if (
    !signature ||
    !secret ||
    !verifyWebhookSignature(body, signature, secret)
  ) {
    return Response.json({ error: "Invalid signature" }, { status: 401 });
  }

  const payload = JSON.parse(body);
  // payload.entry.kind is "page" or "content-entry"

  return Response.json({
    status: "succeeded",
    summary: "Draft updated",
    draftContentPatch: {
      draftMetaDescription: "A concise search description.",
    },
  });
}
```

The request includes `runId`, `siteId`, `workflowId`, `contentType`, `entryId`, `trigger`, and an `entry` snapshot.

## Return a Result

For immediate changes, return:

```json
{
  "status": "succeeded",
  "summary": "Updated the draft",
  "draftContentPatch": {}
}
```

For longer work, return:

```json
{
  "status": "accepted",
  "externalJobId": "job_123",
  "summary": "Started"
}
```

Then call the management API when the job completes:

```bash
curl -X POST "$RIVERBANK_DASHBOARD_URL/api/sdk/$RIVERBANK_SITE_ID/workflows/runs/$RUN_ID/status" \
  -H "Authorization: Bearer $RIVERBANK_MGMT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"succeeded","summary":"Done","draftContentPatch":{"draftMetaTitle":"Updated title"}}'
```

To report failure, return or callback with:

```json
{
  "status": "failed",
  "error": "Could not generate metadata"
}
```

## Draft Patch Rules

Content entry workflows patch draft content fields.

Page workflows patch route metadata only:

- `draftTitle`
- `draftSlug`
- `draftStatus`
- `scheduledPublishAt`
- `draftMetaTitle`
- `draftMetaDescription`
- `canonicalUrl`
- `ogImageId`

If the workflow returns no `draftContentPatch`, it must make changes through management API endpoints or leave the draft unchanged.

## Verification

1. Run `riverbankcms push-config`.
2. Open the matching entry or page in the dashboard.
3. Confirm the workflow section appears only when the config contains matching manual workflows.
4. Run the workflow and confirm the editor stays disabled until the endpoint returns or the async callback completes.
5. Confirm the draft reflects any returned patch.
