Skip to content

Use Cases & Patterns

HARP is intentionally a 1:1 protocol: one request, one pair_id, one human decides. But agent platforms can compose multiple HARP calls to implement sophisticated authorization patterns. This page covers the most common patterns with architecture diagrams and code.

The default HARP flow. One agent, one human. The agent sends an approval request and the human approves or denies on their phone.

When to use: Individual developers using AI agents, personal automation, small teams with a single point of contact.

Example: A developer running Claude Code locally. The agent wants to execute rm -rf /tmp/build. HARP sends the request, the developer reviews on their phone, approves with Face ID.

import { Authorizer, loadPairing } from "@humanauth/sdk";
const pairing = await loadPairing("my-laptop");
const auth = new Authorizer(pairing);
const result = await auth.requestApproval({
action: "bash_execute",
description: "Remove temporary build directory",
parameters: { command: "rm -rf /tmp/build" },
reasoning: "Build cache is stale and consuming 2GB",
ttl: 300,
});
if (result.decision === "approved") {
await execCommand("rm -rf /tmp/build");
}

The agent platform sends parallel HARP requests to multiple pair_ids and applies quorum logic locally. The relay knows nothing about the grouping.

When to use: Financial transactions over a threshold, production deployments, compliance-sensitive operations that require multiple sign-offs.

Architecture:

Agent Platform
|-- HARP request --> pair_id_1 (CFO) --> APPROVED
|-- HARP request --> pair_id_2 (CTO) --> APPROVED
+-- HARP request --> pair_id_3 (VP Eng) --> DENIED
Platform logic: 2-of-3 required --> APPROVED

Implementation:

import { Authorizer, loadPairing } from "@humanauth/sdk";
// Load pairings for each approver
const approvers = [
{ name: "cfo", pairing: await loadPairing("cfo") },
{ name: "cto", pairing: await loadPairing("cto") },
{ name: "vp-eng", pairing: await loadPairing("vp-eng") },
];
const QUORUM = 2; // 2-of-3 required
const request = {
action: "wire_transfer",
description: "Transfer $50,000 to vendor account",
parameters: { amount: 50000, recipient: "ACME Corp", account: "****7890" },
ttl: 600,
};
// Send all requests in parallel
const results = await Promise.allSettled(
approvers.map(async ({ name, pairing }) => {
const auth = new Authorizer(pairing);
const result = await auth.requestApproval(request);
return { name, ...result };
})
);
// Count approvals
const approved = results
.filter((r) => r.status === "fulfilled" && r.value.decision === "approved")
.map((r) => r.value);
if (approved.length >= QUORUM) {
console.log(`Quorum reached (${approved.length}/${approvers.length}). Proceeding.`);
await executeTransfer();
} else {
console.log(`Quorum not reached. ${approved.length}/${QUORUM} approvals.`);
}

Considerations:

  • Each approver sees the same request context (or the platform can customize the description per approver)
  • Each response is independently signed with Ed25519 — cryptographic non-repudiation per approver
  • The platform should handle partial responses (e.g., 2 approved but 1 expired)
  • Consider including “1 of 3 approved so far” in the context field for subsequent requests

The agent platform sends a HARP request to the primary approver first. If they do not respond within a shorter TTL, the platform escalates to the next person in the chain.

When to use: On-call workflows, time-sensitive operations, manager escalation for denied requests.

Architecture:

Agent Platform
|-- HARP request (TTL=120s) --> pair_id_1 (on-call eng)
| +-- EXPIRED (no response in 2 min)
|-- HARP request (TTL=180s) --> pair_id_2 (team lead)
| +-- APPROVED
+-- Done

Implementation:

import { Authorizer, loadPairing } from "@humanauth/sdk";
const escalationChain = [
{ name: "on-call-eng", ttl: 120 },
{ name: "team-lead", ttl: 180 },
{ name: "eng-director", ttl: 300 },
];
const baseRequest = {
action: "deploy_hotfix",
description: "Deploy security hotfix to production",
parameters: { service: "auth", version: "1.2.1-hotfix" },
};
for (const { name, ttl } of escalationChain) {
const pairing = await loadPairing(name);
const auth = new Authorizer(pairing);
const result = await auth.requestApproval({
...baseRequest,
reasoning: name === escalationChain[0].name
? "Critical security patch"
: `Escalated: ${escalationChain[0].name} did not respond`,
ttl,
});
if (result.decision === "approved") {
console.log(`Approved by ${name}. Deploying hotfix.`);
await deployHotfix();
break;
} else if (result.decision === "denied") {
console.log(`Denied by ${name}: ${result.reason}`);
break; // Explicit denial stops the chain
}
// If expired, continue to next person in chain
console.log(`${name} did not respond within ${ttl}s. Escalating...`);
}

Considerations:

  • Explicit denials should stop the chain — do not escalate a denied request
  • Include escalation context in the reasoning field so the next approver knows why they are being asked
  • The chain should have a maximum depth (usually 3-4 levels)

The agent collects multiple pending actions and sends them as a single HARP request for batch review. This reduces notification fatigue for non-urgent operations.

When to use: Nightly batch jobs, agents that queue up non-urgent actions for periodic human review, reducing notification fatigue.

Architecture:

Agent Platform
+-- HARP request:
action: "batch_approve"
parameters:
items:
- { action: "file_delete", path: "/tmp/old-logs" }
- { action: "db_migrate", schema: "users_v3" }
- { action: "deploy", service: "api", version: "2.1.0" }

Implementation:

import { Authorizer, loadPairing } from "@humanauth/sdk";
const pairing = await loadPairing("ops-lead");
const auth = new Authorizer(pairing);
const pendingActions = [
{ action: "file_delete", path: "/tmp/old-logs", size: "2.3GB" },
{ action: "db_migrate", schema: "users_v3", rows_affected: 15000 },
{ action: "deploy", service: "api", version: "2.1.0" },
];
const result = await auth.requestApproval({
action: "batch_approve",
description: `Batch of ${pendingActions.length} pending actions from overnight run`,
parameters: { items: pendingActions },
reasoning: "Nightly maintenance batch collected between 00:00-06:00 UTC",
ttl: 600,
});
if (result.decision === "approved") {
for (const action of pendingActions) {
await executeAction(action);
}
}

Considerations:

  • The current protocol is all-or-nothing per request. For per-item granularity, send separate requests
  • Include clear summaries in the description field so the reviewer can quickly assess the batch
  • The HARP app renders the full parameters object, so structured batch data displays clearly

Agents running in CI/CD pipelines, cron jobs, or multi-agent orchestration systems. No human watches a terminal — HARP provides the approval channel via push notifications.

When to use: Overnight batch jobs, scheduled agents, CI/CD deployment gates, multi-agent systems that need human sign-off for critical steps.

Architecture:

CI/CD Pipeline
+-- Agent step: "deploy to production"
+-- HARP request --> pair_id (DevOps lead's phone)
+-- Push notification at 3 AM
+-- Approve from bed --> deploy proceeds

Implementation (GitHub Actions example):

.github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Request deployment approval via HARP
run: |
npx @humanauth/cli request \
--name "deploy-approver" \
--action "deploy_production" \
--description "Deploy ${{ github.sha }} to production" \
--ttl 600
env:
HARP_PAIRINGS_DIR: ${{ secrets.HARP_PAIRINGS_DIR }}
- name: Deploy
run: ./scripts/deploy.sh

Considerations:

  • Push notifications are critical — the human is not watching anything
  • Set generous TTL values (max 600s) since response time may be slower at off-hours
  • Handle expired responses gracefully: retry later, alert via a backup channel, or fail safe
  • Store the pairing file as a CI secret (it contains the encryption keys)

A single HARP app on the user’s phone, paired with multiple agent platforms. One app manages all agent authorizations.

When to use: Any user with multiple AI agent integrations — personal laptop, work agents, home automation, trading bots.

Architecture:

HARP App (user's phone)
|-- Paired with: Claude Code (personal laptop)
|-- Paired with: Company AI Agent (work)
|-- Paired with: Home Automation Agent
+-- Paired with: Trading Bot

Each pairing has independent keys. Compromising one platform does not affect others. The app’s Home screen groups pending requests by platform for clarity, and any pairing can be revoked independently.

Setup:

Terminal window
# Pair with personal laptop
humanauth pair --name "personal-laptop" --relay https://relay.humanauth.ai
# Pair with work agent
humanauth pair --name "work-agent" --relay https://relay.humanauth.ai
# Pair with home automation
humanauth pair --name "home-auto" --relay https://relay.humanauth.ai
# List all pairings
humanauth list
NAME PAIR_ID RELAY PAIRED
personal-laptop pair_a1b2c3d4e5f6 https://relay.humanauth.ai 2026-04-10
work-agent pair_x7y8z9w0v1u2 https://relay.humanauth.ai 2026-04-11
home-auto pair_m3n4o5p6q7r8 https://relay.humanauth.ai 2026-04-12

The user denies a request with a reason, and the agent adapts its approach based on the feedback. Denial is not a dead end — it is a course correction.

When to use: Interactive agent workflows where the human wants to guide the agent rather than simply block it.

Architecture:

Agent: "Run: rm -rf /home/user/projects"
+-- HARP request --> user
+-- DENIED, reason: "Too broad. Only delete /home/user/projects/tmp"
+-- Agent adapts: "Run: rm -rf /home/user/projects/tmp"
+-- HARP request --> user
+-- APPROVED

Implementation:

import { Authorizer, loadPairing } from "@humanauth/sdk";
const pairing = await loadPairing("my-laptop");
const auth = new Authorizer(pairing);
let command = "rm -rf /home/user/projects";
const MAX_RETRIES = 3;
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
const result = await auth.requestApproval({
action: "bash_execute",
description: `Execute: ${command}`,
parameters: { command },
reasoning: attempt === 0
? "Cleaning up disk space"
: `Adjusted based on feedback: ${previousReason}`,
ttl: 300,
});
if (result.decision === "approved") {
await execCommand(command);
break;
}
if (!result.reason) {
console.log("Denied without feedback. Stopping.");
break;
}
// Pass the denial reason to the LLM to adapt
const previousReason = result.reason;
command = await llm.adjustCommand(command, result.reason);
console.log(`Adjusted command to: ${command}`);
}

Considerations:

  • The reason field in the denial response is passed back to the agent — no protocol changes needed
  • Set a maximum retry count to prevent infinite loops
  • The agent (or its orchestrating LLM) interprets the feedback and adjusts the action
  • Each retry is a fresh HARP request with a new request_id

Both sides of the protocol retain full plaintext independently. The relay retains zero plaintext. This architecture is purpose-built for compliance environments.

Platform side: The SDK automatically logs to ~/.harp/audit/YYYY-MM-DD.jsonl — every request sent, every response received, full plaintext.

{"timestamp":"2026-04-12T10:30:00Z","request_id":"req_abc123","pair_id":"pair_xyz","action":"deploy_production","description":"Deploy API v2.1","decision":"approved","signature":"base64..."}
{"timestamp":"2026-04-12T10:45:00Z","request_id":"req_def456","pair_id":"pair_xyz","action":"db_migrate","description":"Migrate users table","decision":"denied","reason":"Not during peak hours"}

App side: On-device history stored in SQLite. Full decrypted context for every request. Exportable for personal records.

When to use: SOC2, HIPAA, SOX, or any environment requiring proof of human authorization for agent actions.

Considerations:

  • The relay retains zero plaintext — audit logs exist only at the endpoints
  • For enterprise deployments, platform-side audit logs can be shipped to SIEM systems (Splunk, Datadog, etc.)
  • Ed25519 signatures provide cryptographic non-repudiation: proof that a specific device (and biometric-authenticated human) approved a specific action at a specific time
  • Each approval is independently verifiable — signatures can be checked without contacting the relay