{
  "markdown": "# Gatewerk\n\n**The open source review layer for AI agents.**\n\n> One inbox where your agents' actions stop and wait for a person.\n> Review the exact payload, edit it, decide. Every decision on the record.\n\nYour AI agent drafts an email, generates a report, or initiates a\ntransaction. Before it reaches the real world, a human reviews it in a\nstructured form, edits what needs fixing, and sends the decision back.\nThe agent learns from what humans changed.\n\nWorks with any agent framework. Self hosted. Open source (AGPL-3.0;\nclient SDKs are Apache-2.0). See [LICENSING.md](LICENSING.md).\n\n---\n\n## Why Gatewerk?\n\nYour agents act for you: they send the proposals, run the campaigns,\nhandle the invoices, write the replies. Before an action leaves the\nbuilding, a human should decide. Gatewerk is the review layer between\nyour agents and the real world. Human judgment enters the pipeline,\nexactly where you choose.\n\n- **Structured review forms**: not just \"approve/deny\", but rich templates with editable fields.\n- **Feedback memory**: agents query past decisions to improve over time\n- **Self hosted**: your server, your data, no SaaS dependency\n- **Protocol first**: works with any agent framework via REST API, TypeScript/Python SDKs, or MCP\n\n## Use Cases\n\n### Before Sending\nEmail drafts, customer support replies, Slack messages, SMS campaigns, outreach to candidates, proposals to clients\n\n### Before Executing\nCode deployments, database migrations, financial transactions, infrastructure changes, API calls to third-party services\n\n### Before Publishing\nBlog posts, social media content, ad campaigns, product listings, documentation updates, press releases, reports\n\n### Before Approving\nExpense reports, refund requests, access permissions, contract terms, insurance claims, purchase orders\n\nBetween the gates, agents can also bring you the judgment calls: classify the edge case, pick the path, resolve the exception. Gatewerk makes human judgment callable: agents ask a structured question, you answer typed, and the pipeline continues.\n\n## How It Works\n\n```mermaid\nsequenceDiagram\n    participant Agent\n    participant API as Gatewerk API\n    participant Reviewer as Reviewer Dashboard\n    participant Webhook as Agent via webhook\n\n    Agent->>API: POST /reviews (template + payload)\n    API->>Reviewer: Review appears in inbox\n    Reviewer->>API: Approve / Reject / Edit\n    API->>Webhook: Decision + edited payload\n    Webhook->>Agent: Deliver decision\n    Agent->>API: GET /feedback (learn from past decisions)\n```\n\n## Works With Any Agent Framework\n\nGatewerk is protocol-first, not framework-specific. Your agent framework already has a way to make HTTP calls or use MCP: that's all you need.\n\n| Framework | Integration Path |\n|-----------|-----------------|\n| **LangChain / LangGraph** | Python SDK |\n| **CrewAI** | Python SDK |\n| **AutoGen / Semantic Kernel** | Python SDK |\n| **OpenAI Agents SDK** | TypeScript or Python SDK |\n| **Vercel AI SDK** | TypeScript SDK |\n| **Claude / Cursor / Windsurf** | MCP Server |\n| **n8n / Make / Zapier** | REST API + Webhooks (n8n community node published as `n8n-nodes-gatewerk`) |\n| **Dify** | REST API |\n| **Custom agents** | REST API, TypeScript SDK, Python SDK, or MCP |\n\n## A review, not a yes/no ping\n\nWithout a review layer, every agent grows its own approval hack: a Slack button here, a weekend dashboard there, a shared sheet nobody audits. You end up building the same thing over and over, once per workflow. Gatewerk is that layer built once, properly:\n\n- **Structured forms**: Reviewers see rich templates with text, markdown, JSON, images, not a yes/no dialog\n- **Edit in place**: Reviewers fix the agent's output directly, not just reject it\n- **Built for more than one human**: chains route multi-step approvals, reviews are claimed and reassigned, and the record shows exactly who decided what\n- **Feedback loop**: Agents query past decisions via API to improve over time\n- **Suggested vs Approved**: Every field tracks what the agent proposed vs what the human approved, so agents learn exactly what changed\n- **Audit everything**: HMAC-signed immutable log of every action, included free\n- **Your infrastructure**: Self-hosted, open source (AGPL-3.0), no vendor lock-in\n\n## Quick Start\n\n### 1. Start Gatewerk\n\nDocker is the supported path. `quickstart.sh` writes a `.env` with freshly generated secrets, pulls the published images, applies migrations, seeds demo data, and waits until the API is healthy.\n\n```bash\ngit clone https://github.com/gatewerk/gatewerk.git\ncd gatewerk\n./scripts/quickstart.sh\n```\n\nDashboard: `http://localhost:8880`\nAPI: `http://localhost:3100`\n\nRequirements: Docker and `openssl`. Nothing else. The images are multi-arch, so amd64 and arm64 both pull prebuilt.\n\nTo compile the images from this checkout instead of pulling them, run `./scripts/quickstart.sh --build`.\n\nTo run the API and dashboard from source with `pnpm` and hot reload, see [Contributing](#contributing).\n\n### 2. Log in and create an API key\n\nOpen the dashboard and log in with the seed admin account:\n\n- Email: `admin@gatewerk.local`\n- Password: `admin123`\n\nOn first login the dashboard will prompt you to change the password: pick something secure.\n\nThe seed script (`packages/db/src/seed.ts`) also creates a default project, 6 starter templates (Proposal Review, Email Review, Code Deploy, Content Approval, Expense Report, Customer Reply), and a default API key. The raw key is printed once, by the seed container. Read it back with:\n\n```bash\ndocker compose logs gatewerk-seed\n```\n\nSave that key if you want to skip the next step. It starts with `gwk_`.\n\nTo create a fresh API key from the dashboard, navigate to **Settings → Project → API Keys**, click **New**, scope it for your agent, and copy the value. The raw key is shown only once. Use it wherever you see `YOUR_API_KEY` below.\n\n### 3. Create a Template\n\nTemplates define what gets reviewed. Create one via the dashboard or API:\n\n```bash\ncurl -X POST http://localhost:3100/api/v1/templates \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"slug\": \"quickstart-email-review\",\n    \"name\": \"Email Review\",\n    \"instructions\": \"Check tone and accuracy before approving.\",\n    \"fields\": [\n      { \"name\": \"subject\", \"type\": \"text\", \"label\": \"Subject\", \"editable\": true },\n      { \"name\": \"body\", \"type\": \"markdown\", \"label\": \"Body\", \"editable\": true },\n      { \"name\": \"recipient\", \"type\": \"text\", \"label\": \"To\", \"readonly\": true }\n    ],\n    \"actions\": [\"approve\", \"reject\", \"request_changes\"],\n    \"default_priority\": \"normal\",\n    \"timeout_seconds\": 86400,\n    \"timeout_action\": \"expire\",\n    \"auto_approve\": false\n  }'\n```\n\nThe three action types are `approve`, `reject`, and `request_changes` (non-terminal, requires reviewer feedback). Inline editing happens automatically on any field with `\"editable\": true`, there is no separate \"edit\" action. Actions can also be declared as `{ type, label, value }` objects for custom button labels.\n\n### 4. Send a Review Request\n\nBoth SDKs and the MCP server are published. Install them with `npm install gatewerk`, `pip install gatewerk`, or `npx @gatewerk/mcp`. No build from source required.\n\nThe two mechanisms for delivering decisions back to your agent are distinct:\n\n- `callback_url` (per-review, used below): a one-shot HTTP destination attached to a single review. Fires the three event types documented in step 5.\n- Project-level webhooks (in **Settings → Webhooks**): event subscriptions that fire across all reviews in a project. Use these when you have one agent/service receiving decisions for many reviews.\n\nPick one, or use both. The payload shape is identical.\n\n**TypeScript:**\n\n```typescript\nimport { createClient } from \"gatewerk\";\n\nconst gw = createClient({\n  apiKey: \"gwk_...\",\n  url: \"http://localhost:3100\",\n});\n\nconst { data, error } = await gw.reviews.create({\n  template: \"quickstart-email-review\",\n  payload: {\n    subject: \"Q1 Report\",\n    body: \"Revenue grew 23% YoY...\",\n    recipient: \"ceo@company.com\",\n  },\n  callback_url: \"https://example.com/webhook\",\n  priority: \"high\",\n});\n```\n\n**Python:**\n\n```python\nfrom gatewerk import create_client\n\ngw = create_client(api_key=\"gwk_...\", url=\"http://localhost:3100\")\n\nreview = gw.reviews.create(\n    template=\"quickstart-email-review\",\n    payload={\n        \"subject\": \"Q1 Report\",\n        \"body\": \"Revenue grew 23% YoY...\",\n        \"recipient\": \"ceo@company.com\",\n    },\n    callback_url=\"https://example.com/webhook\",\n    priority=\"high\",\n)\n```\n\n**MCP (for Claude, Cursor, Windsurf, etc.):**\n\n```json\n{\n  \"mcpServers\": {\n    \"gatewerk\": {\n      \"command\": \"npx\",\n      \"args\": [\"@gatewerk/mcp\"],\n      \"env\": {\n        \"GATEWERK_URL\": \"http://localhost:3100\",\n        \"GATEWERK_API_KEY\": \"gwk_...\"\n      }\n    }\n  }\n}\n```\n\n### 5. Receive the Decision\n\nWhen a reviewer acts on a review, Gatewerk POSTs one of three event types to your `callback_url`:\n\n- `review.decided`: reviewer approved or rejected (terminal; may include edits)\n- `review.retried`: reviewer requested changes and sent feedback (non-terminal; your agent regenerates and submits a new version)\n- `review.expired`: timeout reached with no decision (terminal)\n\nExample `review.decided` payload:\n\n```json\n{\n  \"type\": \"review.decided\",\n  \"review_id\": \"gw_rev_abc123\",\n  \"decision\": \"edited\",\n  \"decided_at\": \"2026-03-11T10:30:00Z\",\n  \"was_edited\": true,\n  \"suggested_value\": { \"subject\": \"Q1 Report\", \"body\": \"Revenue grew 23% YoY...\" },\n  \"approved_value\": { \"subject\": \"Q1 2026 Report\", \"body\": \"Revenue: $4.2M (+23% YoY)...\" },\n  \"reviewer\": \"alice@company.com\",\n  \"feedback\": \"Improved title and added specific numbers\",\n  \"action_value\": \"approve\",\n  \"action_label\": \"Approve\",\n  \"auto_approved\": false\n}\n```\n\nThe `decision` field is one of `approved | rejected | edited | retried | expired`. `action_value` and `action_label` carry the exact button the reviewer clicked (useful for custom action labels). `auto_approved: true` indicates the review was auto-approved by template config rather than a human.\n\n**Action → decision mapping:**\n\n| Reviewer action (template action type) | Resulting `decision` in webhook |\n|---|---|\n| `approve` (no field edits) | `approved` |\n| `approve` (with field edits) | `edited` + `was_edited: true` |\n| `reject` | `rejected` |\n| `request_changes` | `retried` (via `review.retried` event) |\n| timeout reached, no decision | `expired` (via `review.expired` event) |\n\n**Retry round-trip** (for `review.retried`): your agent receives feedback → regenerates output → calls `PUT /api/v1/reviews/:id` with the new payload → review returns to the reviewer's inbox as a new version.\n\nRequests are signed with HMAC-SHA256. Two signature headers are sent on every delivery:\n\n- `X-Webhook-Signature: sha256=<hex>`: v1 legacy envelope, `hex = HMAC(body, secret)`. Simple to verify; does not prevent replay.\n- `X-Webhook-Signature-V2: t=<unix-seconds>,v1=<hex>`: v2 replay-safe envelope, `hex = HMAC(\\`${t}.${body}\\`, secret)`. Verify `t` is within your freshness window (commonly ±300s), then recompute the hex and constant-time compare.\n\nReceivers that care about replay protection should parse v2, enforce a freshness check on `t`, and compare against `v1` within the v2 header. Receivers who only need authenticity may stay on v1 indefinitely: the v1 header is unchanged since v1.0.\n\nOther headers: `X-Webhook-Event` (event type), `X-Webhook-Id` (stable idempotency key across retries; use it for receiver-side dedup), `X-Request-Id` (correlation). Deliveries retry with exponential backoff; after 5 failures the delivery is marked failed.\n\n### 6. Learn from Feedback\n\nAgents query past decisions to improve:\n\n```typescript\nconst { data } = await gw.feedback.query({\n  template: \"quickstart-email-review\",\n  outcome: \"edited\",\n  limit: 10,\n});\n\n// data.data contains past reviews with suggested vs approved values\n// Use this to improve future outputs\n```\n\n## Features\n\n| Feature | Description |\n|---------|-------------|\n| **Review Templates** | Schema-driven forms with text, markdown, JSON, image, number, boolean, select fields |\n| **Rich Actions** | Three action types (`approve`, `reject`, `request_changes`) with custom labels. Inline field editing on `editable: true` fields. |\n| **Feedback Memory** | Agents query historical decisions via API |\n| **Webhook Notifications** | Event-driven webhooks with HMAC-SHA256 signing |\n| **Audit Trail** | HMAC-signed immutable log of every action |\n| **Priority Routing** | Low / normal / high / critical with visual indicators |\n| **Timeout Policies** | Auto-approve, auto-reject, or expire after deadline |\n| **Review Versioning** | Agent submits updated versions after retry feedback |\n| **Chains** | Multi-step approvals: each step is its own review, assigned to a user, a role, or an external signer |\n| **Decisions by link** | Share one review by expiring link; a client or outside counsel decides with no account and no seat |\n| **Team inbox** | Invite teammates with roles; claim, release, and reassign reviews so ownership is always explicit |\n| **MCP Server** | Works with Claude, Cursor, Windsurf, and any MCP-compatible agent |\n| **TypeScript SDK** | `npm install gatewerk`: resource-based client with `{ data, error }` responses |\n| **Python SDK** | `pip install gatewerk`: typed exceptions, Pythonic API |\n| **Self-Hosted** | `./scripts/quickstart.sh`: your server, your data |\n\n## Architecture\n\n```\napps/\n  api/                  — Express API (REST endpoints, webhook delivery, timeout worker)\n  web-next/             — React dashboard (review inbox, forms, settings, metrics)\npackages/\n  db/                   — Drizzle ORM (PostgreSQL schema)\n  sdk-ts/               — TypeScript SDK (npm: gatewerk)\n  sdk-py/               — Python SDK (pip: gatewerk)\n  mcp/                  — MCP server (npx @gatewerk/mcp)\n  n8n-nodes-gatewerk/   — n8n community node (npm: n8n-nodes-gatewerk)\n  shared/               — HRP protocol types, ID generator, error classes\ndocker/                 — Dockerfiles, nginx, compose\n```\n\n## API Overview\n\nAll resources use prefixed IDs (`gw_rev_`, `gw_tpl_`, `gw_prj_`) and consistent response envelopes.\n\n**Core endpoints:**\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `POST` | `/api/v1/reviews` | Create a review request |\n| `GET` | `/api/v1/reviews` | List reviews (filterable) |\n| `GET` | `/api/v1/reviews/:id` | Get review details |\n| `POST` | `/api/v1/reviews/:id/decide` | Submit a decision, body: `{ \"decision\": \"approved\" }` (`approved \\| rejected \\| edited \\| confirmed \\| vetoed`), plus optional `feedback` and `edited_payload` |\n| `POST` | `/api/v1/reviews/:id/retry` | Request retry with feedback |\n| `PUT` | `/api/v1/reviews/:id` | Update review (new version) |\n| `GET` | `/api/v1/feedback` | Query past decisions |\n| `GET` | `/api/v1/templates` | List/create/update/delete templates |\n| `GET` | `/api/v1/audit` | Query audit log |\n| `GET` | `/api/v1/stats` | Review metrics |\n\n**Management endpoints** (dashboard session auth):\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| `*` | `/api/v1/settings/api-keys` | CRUD + rotate API keys |\n| `*` | `/api/v1/settings/webhooks` | CRUD project-level webhook subscriptions |\n| `*` | `/api/v1/settings/notifications` | CRUD notification channels |\n| `GET`/`POST` | `/api/v1/settings/hmac-secret` | Reveal + rotate HMAC signing secret |\n| `*` | `/api/v1/settings/team` | Invite + manage team members |\n\nFull OpenAPI spec at `/api/v1/openapi.json`. Postman collection at `/api/v1/postman.json`.\n\n## Configuration\n\nKey environment variables (see `.env.example` for the full list, including log level, rate limits, media storage paths, and cloud-mode toggles):\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `DATABASE_URL` | `postgresql://gatewerk:gatewerk@localhost:5432/gatewerk` | PostgreSQL connection string |\n| `HMAC_SECRET` | `dev-secret` | Secret for webhook HMAC signing. Rotate via `POST /api/v1/settings/hmac-secret/rotate`. |\n| `JWT_SECRET` | `dev-jwt-secret` | Secret for reviewer session tokens |\n| `UI_ORIGIN` | `http://localhost:5173` | CORS origin for the dashboard |\n| `PORT` | `3100` | API server port |\n\n**Fixed defaults (not env-configurable today):**\n\n- Review timeout: `86400s` (24h) default, configurable per template (`timeout_seconds`).\n- Webhook retries: 5 attempts with exponential backoff; terminal failure marks the delivery failed. (Alert notifications fire only for failed `review.vetoed` and `review.confirmed` deliveries, where a lost decision has agent-side consequences.)\n- Payload size limit: 1 MB per webhook delivery; larger bodies are truncated with `review_url` included.\n\n## HRP Protocol\n\nGatewerk implements the **Human Review Protocol (HRP)**: an open specification for agent-to-human communication. The protocol defines how agents request reviews, how humans respond, and how decisions flow back.\n\nRead the draft spec: [docs/protocol/hrp-v1.md](docs/protocol/hrp-v1.md)\n\n## Philosophy\n\n> Work done for humans is decided by humans.\n\nAgents draft, execute, route, and carry. That is the operational load,\nand agents should take as much of it as you trust them with. The\ndecision, and the ownership that comes with it, stays human. Gatewerk is\nthe review layer where a human decides before an agent's action reaches\nthe world.\n\nThree commitments follow:\n\n- **Framework and platform agnostic.** The belief is about humans and\n  decisions, not about any AI vendor or stack. Gatewerk works with every\n  agent framework and depends on none.\n- **Open source.** An audit trail you cannot inspect is marketing. The\n  record of your judgment lives on your infrastructure, under a license\n  that keeps it open.\n- **Decision points designed by a human and decided by a human.** You\n  choose where judgment enters the pipeline, and you exercise it. A human\n  decides, never a model.\n\nThe full doctrine: [docs/philosophy.md](docs/philosophy.md).\n\n## Licensing\n\nThe server and dashboard are AGPL-3.0-only; the client SDKs are Apache-2.0; `ee/` is proprietary. Self-hosting for your own use is unrestricted. See [LICENSING.md](LICENSING.md).\n\n## Contributing\n\nWe are not yet accepting external contributions to the AGPL-licensed server; issues and discussion are very welcome.\n\nRunning from source gives you hot reload on both the API and the dashboard. Postgres still comes from Docker; everything else runs on your machine.\n\n```bash\n# Development setup\ngit clone https://github.com/gatewerk/gatewerk.git\ncd gatewerk\ncp .env.example .env\npnpm install\ndocker compose -f docker/docker-compose.dev.yml up -d  # PostgreSQL only\nln -sf ../../.env apps/api/.env                        # Share root .env with the packages that\nln -sf ../../.env packages/db/.env                     # need it: API server and drizzle-kit\npnpm --filter @gatewerk/db run push                    # Apply schema\ncd packages/db && bun run src/seed.ts && cd ../..      # Seed demo data and print an API key\npnpm run dev                                           # Start API + dashboard\n```\n\nDashboard: `http://localhost:5174`\nAPI: `http://localhost:3100`\n\nTests: `pnpm -r test`\n\n### The empty `ee/` directory\n\n`ee/` is a git submodule pointing at a private repository that holds the\ncommercial hosted-service code. After a normal `git clone` it is an empty\ndirectory, and that is the expected state.\n\nYou do not need it. Everything above (install, typecheck, lint, tests, both\nDocker images) is built and tested without it, and CI runs that way on every\npush so it stays true. Do not run `git clone --recurse-submodules`; the\nsubmodule is private, so that will just fail on authentication.\n\n## License\n\nThe server and dashboard are AGPL-3.0-only. Client SDKs (`sdk-ts`, `sdk-py`, `mcp`, `n8n-nodes-gatewerk`) are Apache-2.0. The `ee/` submodule is proprietary and lives in a separate private repository. See [LICENSING.md](LICENSING.md) for details.\n\n---\n\nLeveraged [Claude Code](https://claude.com/claude-code).\n",
  "bytes": 20028,
  "sha": "ea102462c8d52ccd4f4c7b8622f5d270c57230969067a1bb893481864dc65957",
  "repo_slug": "gatewerk/gatewerk",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_gatewerk_mcp_dbae1a75/readme"
}