Impri documentation

Add a human approval step to any AI agent. An agent proposes an action, a person approves or rejects it in the inbox, and only then does it run.

What is Impri

Impri is a human-in-the-loop gate for AI agents and automations. It does not act on its own — it holds an agent's proposed action until a human says yes. Two moving parts:

Agents integrate over a small REST API or the MCP server. It is open-core (MIT) and self-hostable.

Two ways to use it

Self-host

docker compose up and you have the full core running locally in a few minutes. This is the complete, production-ready path today. Start below.

Hosted cloud (beta)

A hosted instance runs at app.impri.dev / api.impri.dev. Self-serve signup isn't open yet — reach out on GitLab for early access. Everything below applies; just swap the base URL.

Quickstart (self-host)

From zero to your first approved action in under five minutes. You need Docker and curl.

1. Start Impri

git clone https://gitlab.com/sekera.radim/impri.git
cd impri

# set a strong secret for signed webhooks
export WEBHOOK_SECRET=$(openssl rand -hex 32)

docker compose up -d

The API runs on http://localhost:8484 and the web inbox on http://localhost:8080. On first start the server prints a one-time bootstrap admin key to its log:

docker compose logs server | grep "Admin API Key"
# Admin API Key: im_...   (shown once, then hashed — store it now)
export ADMIN_KEY="im_..."

Paste that key into the web inbox login to sign in.

2. Create a key for your agent

The bootstrap key has admin scope. Mint a narrower actions key for the agent so you can rotate it independently:

curl -s -X POST http://localhost:8484/v1/keys \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-agent","scopes":["actions"]}'
# → { "key": "im_...", ... }   store as $AGENT_KEY

3. Propose an action

This is the single call your agent makes before doing anything consequential:

curl -s -X POST http://localhost:8484/v1/actions \
  -H "Authorization: Bearer $AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "email.send",
    "title": "Send welcome email to jane@acme.com",
    "preview": { "format": "markdown", "body": "To: jane@acme.com\n\nWelcome aboard!" },
    "expires_in": 3600,
    "editable": ["preview.body"]
  }'
# → { "id": "act_...", "status": "pending", ... }

4. Approve it

Open the inbox at http://localhost:8080. The pending action shows up as a card with its preview. Click Approve or Reject. If you set editable, you can edit the content first — the agent receives the final, human-edited version.

5. Act only if approved, then report back

# poll until the decision arrives
curl -s http://localhost:8484/v1/actions/act_... \
  -H "Authorization: Bearer $AGENT_KEY"
# → { "status": "approved", ... }  (or rejected / expired)

# your agent performs the action, then closes the loop:
curl -s -X POST http://localhost:8484/v1/actions/act_.../result \
  -H "Authorization: Bearer $AGENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"executed","detail":"email sent"}'
Lifecycle: pending → approved → executed. If the human rejects, the agent does nothing — that's the whole point.

Wire up an agent

A complete, dependency-free example agent (Node 18+) that runs the whole loop lives in the repo at examples/approval-gated-agent.mjs:

IMPRI_API_KEY=im_your_key \
IMPRI_BASE_URL=http://localhost:8484 \
node examples/approval-gated-agent.mjs

It proposes an action, waits for you to approve it in the inbox, then runs and reports the result. Edit the task and replace performAction() with the real thing you want gated (send an email, deploy, issue a refund, spend money…).

MCP (Claude & other agents)

If your agent is an LLM, you usually don't write the loop by hand — give it the Impri MCP server and it can request approval as a tool call. Published on npm as @impri/mcp:

{
  "mcpServers": {
    "impri": {
      "command": "npx",
      "args": ["-y", "@impri/mcp"],
      "env": { "IMPRI_API_KEY": "im_...", "IMPRI_BASE_URL": "http://localhost:8484" }
    }
  }
}

It exposes tools to push an action, await the decision, report the result, and manage watchers — the same API, agent-native.

Actions

An action is one approval request. Fields:

FieldNotes
kind *Free-form label, e.g. email.send, deploy.
title *One-line summary shown in the inbox.
preview *{ format: "markdown" | "plain" | "diff", body } — what the human reviews.
payloadAny JSON your agent needs back with the decision.
target_urlOptional link to the thing being acted on.
expires_inSeconds until it auto-expires (default 3 days).
editablePaths the human may edit before approving, e.g. ["preview.body"].

States: pending → approved | rejected | expired, then executed | execute_failed once the agent reports back.

Watchers (no code)

A watcher checks a source on a schedule and drops matches into the inbox — the no-code way to feed Impri. Create them in the web UI (Watchers → Create) or via POST /v1/watchers. Three kinds:

KindWatches
reddit_searchA subreddit for posts matching a query.
rssAn RSS/Atom feed for new entries.
url_diffA web page for content changes.

Optional keywords score/filter results before they reach you; a schedule sets how often it runs. Each surfaced item becomes a pending action you review.

Keys & scopes

API keys start with im_ and are hashed at rest. Scopes: admin (full control, billing, keys, watchers) and actions (create actions + report results — give this to agents). Manage keys via /v1/keys.

API reference

Base URL <host>/v1. Auth header Authorization: Bearer im_.... Full spec at /v1/openapi.json.

MethodPathDescription
POST/actionsPropose an action for approval
GET/actionsList actions (?status=pending)
GET/actions/:idGet one action + its decision
POST/actions/:id/decisionApprove or reject
POST/actions/:id/resultReport execution result
GET / POST/watchersList / create watchers
PATCH / DELETE/watchers/:idUpdate / delete a watcher
GET / POST / DELETE/keysList / mint / revoke API keys
GET/project/exportGDPR export of project data
DELETE/project/dataErase project data

Self-hosting

docker compose up runs the server + inbox with a persistent SQLite volume. Key environment variables:

VarPurpose
WEBHOOK_SECRETSigns outbound webhooks (set a strong random value).
BASE_URLPublic URL of the API (used in links).
APP_URLPublic URL of the web inbox (checkout/return links).
SMTP_* / NTFY_*Optional email / ntfy notifications on new approvals.
VAPID_*Optional web-push notifications.
REDIS_URLOptional — Redis-backed rate limiting for multi-instance.
STRIPE_*Optional — only if you run the paid tiers yourself.

Full guide: self-hosting.md (env, backups, reverse proxy).

More docs