Skip to main content

Example: Require Approval for AI Agent API Writes

This example puts an agent in front of a small internal orders API.

  • Reads: the agent looks up orders without asking anyone.
  • Internal notes: writing a note is an autonomous write, so it also runs without asking.
  • Refunds: these require approval. The platform holds the exact call and sends a signed agent.pending_action.created event. A person decides in their own tooling, and only then does the platform execute that call with the integration's stored credential.

The agent never holds the API token.

Everything is Python 3.12+ and the standard library. Download the files into one directory. In a repository checkout, they live under docs/static/examples/governed-writes/.

  • README.md: the full walkthrough.
  • orders-api.openapi.json: the API contract, with x-dt-policy, x-dt-tool-name and x-dt-redact on each operation.
  • orders_api.py: an in-memory stand-in for the internal service.
  • setup.py: imports and publishes the spec through Remote MCP, creates the agent, binds the credential and approver, and runs the readiness preflight.
  • approver.py: the approval tool. It runs as a webhook receiver, a polling loop or one-off commands, with optional Slack buttons.

Run it​

export DT_API_BASE="https://your-deployment.example/api/v1"
read -r -s -p "Organization API key: " DT_API_KEY; export DT_API_KEY
python3 orders_api.py --port 8090 # separate terminal
python3 setup.py import --base-url https://orders.internal.example
# Add the bearer token in the console link that import prints.
python3 setup.py agent
python3 setup.py bind
export DT_AGENT_ID=... # printed by bind
python3 approver.py watch # or: subscribe + serve

Then email the agent about a damaged order. The orders service logs the read and the note at once. The refund appears only after you approve the held call shown by approver.py.

What the example relies on​

  • Approver identity: Use an Organization API key issued by the person who approves. Pending-action resolution records the key's issuer, who must be a configured approver for the agent's binding. Service credentials cannot resolve pending actions.
  • Credential entry: Credentials are entered only in the console. Remote MCP configures integrations, operations, bindings and approvers, but never accepts secrets.
  • Webhook verification: Signed agent events are verified over the raw body and stored in an inbox before any decision. The receiver re-reads current pending actions instead of trusting the event snapshot.
  • Webhook reachability: Delivery goes through the deployment's egress proxy, so the receiver needs a reachable HTTPS URL. approver.py watch polls instead, for environments the platform cannot reach.
  • Decision retries: A repeated decision on an OpenAPI approval returns 409; it never makes a second call. approver.py also sends an Idempotency-Key where extended retry admission is enabled, and otherwise falls back to a plain request.
  • Slack: Slack mode is the customer's own Slack app calling this API. It does not depend on a platform Slack integration. Restrict SLACK_APPROVER_USER_IDS to the key issuer, because the platform attributes every decision to that person.

Known limits​

  • The platform holds an approval-required call without validating its arguments against the published input schema. If an approved call breaks the schema, it fails before dispatch, yet it is currently recorded as an uncertain (unknown) outcome. The example keeps the refund reason free text for that reason. Check the provider before allowing another proposal.
  • A held call stays bound to the integration revision it was proposed under. Publishing a corrected spec does not change calls that are already pending.
  • The readiness preflight checks stored configuration only. Worker execution and network reachability remain unknown until real work runs.

For a laptop-only run on a local development installation (DT_RELEASE_ENV=local), add the service's exact origin to OPENAPI_LOCAL_HTTP_ORIGINS, for example http://127.0.0.1:8090. Then restart the web process and the agent workers. Other environments require a public HTTPS base URL that passes destination validation.