API Reference
TAP exposes three groups of endpoints: agent endpoints (authenticated via X-TAP-Key), admin auth endpoints (public), and admin CRUD endpoints (authenticated via Authorization: Bearer <session_token>).
Agent Endpoints
These endpoints are used by AI agents. All except /health require the X-TAP-Key header.
Recommended agent flow:
- Fetch
/instructionsto learn the setup flow. - Ask the user for the one-time agent API key from the dashboard.
- Call
/agent/bootstrapto verify the key and setup state. - Call
/agent/servicesto discover usable credentials and request templates. - Send requests through
/forward.
GET /instructions
Public agent setup metadata, served as plain text. Intentionally unauthenticated so a user can give an agent only the TAP proxy URL and the agent can learn where to send the human next. (GET / on the bare proxy domain redirects to the dashboard.)
curl https://proxy.tap.human.tech/instructionsResponse (plain text):
# TAP — Tool Authorization Proxy
Proxy: https://proxy.tap.human.tech
Credential proxy for AI agents. Agents forward API calls; TAP injects credentials.
## Quick start
1. GET https://proxy.tap.human.tech/agent/services (header: X-TAP-Key)
2. POST https://proxy.tap.human.tech/forward
## POST /forward headers
X-TAP-Key: <api-key> required
X-TAP-Target: <upstream URL> required (full https:// URL)
X-TAP-Method: GET|POST|… required (upstream HTTP method)
X-TAP-Credential: <service> required (name from /agent/services)
Only these 4 X-TAP-* headers exist. Others return 400.
Always POST to /forward, even for upstream GETs.
Non-TAP headers forward verbatim. Body goes in HTTP body (no X-TAP-Body).
...GET /agent/bootstrap
Authenticated setup check for agents. Use this immediately after the user provides an agent API key. This endpoint does not expose secrets; it only tells the agent whether the key is usable and what to do next.
curl https://proxy.tap.human.tech/agent/bootstrap \
-H "X-TAP-Key: $MY_KEY"Ready response:
{
"protocol": "tap",
"version": 1,
"status": "ready",
"agent_id": "my-agent",
"team_id": "abc-123",
"credential_count": 2,
"dashboard_url": "https://proxy.tap.human.tech/dashboard",
"services_url": "/agent/services",
"forward_url": "/forward",
"logs_url": "/agent/logs",
"agent_action": "Call /agent/services next, then use /forward with the returned request templates.",
"safe_to_retry": true
}If the key is valid but no credentials are assigned, status is needs_credentials and agent_action tells the agent to ask the user to assign credentials in the dashboard.
POST /forward
The core proxy endpoint. Authenticates the agent, evaluates policy, requests approval if needed, injects credentials, forwards the request, and sanitizes the response.
Unified Mode (Recommended)
Reference a credential by name. The proxy handles routing and auth injection automatically.
Headers:
| Header | Required | Description |
|---|---|---|
X-TAP-Key | yes | Agent API key |
X-TAP-Credential | yes | Credential name (no agent-ID prefix — just the plain name) |
X-TAP-Target | yes | Target API URL (or path if relative_target is set) |
X-TAP-Method | no | HTTP method to use upstream (default: GET) |
Example — auto-approved GET:
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $MY_KEY" \
-H "X-TAP-Credential: slack" \
-H "X-TAP-Target: https://slack.com/api/conversations.list" \
-H "X-TAP-Method: GET"Example — POST requiring approval:
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $MY_KEY" \
-H "X-TAP-Credential: slack" \
-H "X-TAP-Target: https://slack.com/api/chat.postMessage" \
-H "X-TAP-Method: POST" \
-H "Content-Type: application/json" \
-d '{"channel": "C123456", "text": "Hello from my agent"}'When approval is required, the proxy responds 202 Accepted immediately with a transaction ID, an approval link, and a poll_url. The agent polls GET /agent/approvals/{txn_id} until the status is forwarded, denied, or timed_out (approval window default: 1 hour). Before making any write call, tell the user what you’re about to send; after the 202, show them the approval link. TAP includes a notification_channel field ("dashboard", "agent_reflected", "telegram", or "matrix") in the 202 response so you can tell the user where the approval prompt went.
Example 202 response:
{
"txn_id": "550e8400-e29b-41d4-a716-446655440000",
"poll_url": "/agent/approvals/550e8400-e29b-41d4-a716-446655440000",
"approval_dashboard_url": "https://app.tap.human.tech/dashboard#/approvals",
"approval_url": "https://app.tap.human.tech/approve/txn/550e8400-...",
"expires_in": 3600,
"status": "pending",
"notification_channel": "agent_reflected",
"agent_hint": "Approval required. Ask the user to open the dashboard or use the direct approval link. Then poll /agent/approvals/{txn_id} to check status."
}Example — sidecar with relative target:
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $MY_KEY" \
-H "X-TAP-Credential: telegram" \
-H "X-TAP-Target: /sendMessage" \
-H "X-TAP-Method: POST" \
-H "Content-Type: application/json" \
-d '{"chat_id": "123", "text": "Hello"}'Legacy Placeholder Mode
Use <CREDENTIAL:name> placeholders in headers or body. The proxy validates placeholder positions, then substitutes real values after approval.
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $MY_KEY" \
-H "X-TAP-Target: https://api.openai.com/v1/chat/completions" \
-H "Authorization: Bearer <CREDENTIAL:openai-key>" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}]}'Placeholders are only allowed in auth-related positions. The proxy rejects requests where credentials appear in non-auth positions (tweet text, email body, etc.) to prevent exfiltration.
Error Responses
| Status | Meaning |
|---|---|
| 202 | Approval required — response carries txn_id, approval_url, and poll_url (not an error; poll for the outcome) |
| 400 | Invalid request (missing headers, unknown X-TAP-* header, placeholder in non-auth position) |
| 401 | Invalid or missing X-TAP-Key |
| 403 | Credential not whitelisted for this agent |
| 429 | Rate limit exceeded |
| 502 | Upstream API error |
| 503 | Transient database error — response includes safe_to_retry: true; retry rather than treating it as auth failure |
All errors return JSON with an error field. Approval denial and timeout are not HTTP errors on /forward — they surface as "denied" / "timed_out" statuses on the poll endpoint.
Agent UX contract for writes:
- Before the call: tell the user what you’re about to post and that they’ll get an approval request.
- On 202: show the user the
approval_url(or relayagent_hint), then pollpoll_url. - On poll status
denied: tell the user it was denied and ask whether to modify and retry. - On poll status
timed_out: tell the user the approval window expired and ask whether to retry — surfacenotification_channelfrom the 202 so they know where the original prompt went.
GET /agent/approvals/:txn_id
Poll the status of a pending approval transaction returned by a 202 from /forward. Requires the same X-TAP-Key that created the transaction.
curl https://proxy.tap.human.tech/agent/approvals/$TXN_ID \
-H "X-TAP-Key: $MY_KEY"Response while pending:
{
"txn_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"created_at": "2026-06-05T14:22:15Z",
"expires_at": "2026-06-05T14:37:15Z"
}status is one of pending, forwarded, denied, or timed_out. When the request is approved and forwarded, the response includes the sanitized upstream result:
{
"txn_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "forwarded",
"created_at": "2026-06-05T14:22:15Z",
"expires_at": "2026-06-05T14:37:15Z",
"response": {
"status": 200,
"headers": [["content-type", "application/json"]],
"body": "{\"ok\":true}",
"body_encoding": "utf-8",
"complete": true
}
}body_encoding is "utf-8" or "base64" (for binary upstream bodies). Returns 404 for unknown transactions and 403 for transactions created by a different agent.
GET /agent/services
Returns available credentials for the authenticated agent. This is the agent’s source of truth for request shape — use the returned request_template rather than guessing URL patterns or header names.
Single key (most common):
curl https://proxy.tap.human.tech/agent/services \
-H "X-TAP-Key: $MY_KEY"Multiple keys — access credentials from several TAP accounts at once:
Pass a comma-separated list of API keys to merge credentials from multiple agents (whether they’re in the same team or different teams) into one response:
curl https://proxy.tap.human.tech/agent/services \
-H "X-TAP-Key: $PERSONAL_KEY,$COMPANY_KEY"In multi-key mode each credential name is prefixed with the agent ID it belongs to (e.g. personal-agent.github, company-agent.slack) so there are no collisions even if two accounts have a credential with the same name. The response includes an accounts map with key_index telling you which key to use per account when calling /forward — keys themselves are never returned.
Single-key response:
{
"agent_id": "my-agent",
"home_team_id": "abc-123",
"services": {
"slack": {
"description": "Slack API",
"target_shape": "full_url",
"target_base": "https://slack.com/api",
"approval": {
"default_decision": "pauses_for_human",
"url_match": "substring_contains",
"rules": [
{ "target": "*", "methods": ["GET", "HEAD"], "decision": "proceeds_immediately" },
{ "target": "*", "methods": ["POST", "PUT", "PATCH", "DELETE"], "decision": "pauses_for_human" }
],
"note": "decision describes what TAP does, not what the agent must do. 'pauses_for_human' means TAP automatically routes the request to a human approver — the agent does not ask for approval itself, it just expects the call to block until a human responds. Rules are evaluated top to bottom; url_override rules (when a credential scopes specific URLs) match by case-sensitive substring containment and win over method rules."
},
"request_template": {
"method": "POST",
"url": "https://proxy.tap.human.tech/forward",
"headers": {
"X-TAP-Key": "$TAP_API_KEY",
"X-TAP-Credential": "slack",
"X-TAP-Target": "https://slack.com/api/<path>",
"X-TAP-Method": "GET|POST|PUT|PATCH|DELETE"
}
}
}
}
}Multi-key response (two keys sent, personal-agent at index 0, company-agent at index 1):
{
"accounts": {
"personal-agent": { "agent_id": "personal-agent", "key_index": 0 },
"company-agent": { "agent_id": "company-agent", "key_index": 1 }
},
"services": {
"personal-agent.github": { "account": "personal-agent", "description": "GitHub", ... },
"company-agent.github": { "account": "company-agent", "description": "GitHub", ... },
"company-agent.slack": { "account": "company-agent", "description": "Slack", ... }
}
}When calling /forward for a multi-key credential, use the key at key_index from the key list you sent, and strip the agent-ID prefix from the credential name:
# For company-agent.slack (key_index 1 → $COMPANY_KEY), credential name is just "slack"
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $COMPANY_KEY" \
-H "X-TAP-Credential: slack" \
-H "X-TAP-Target: https://slack.com/api/chat.postMessage" \
-H "X-TAP-Method: POST" \
-d '{"channel": "C123", "text": "hello"}'Internal details (credential values, sidecar URLs, connector internals) are never returned. Agents should copy the request_template for a service, fill in a valid X-TAP-Target, and put write payloads in the HTTP body.
GET /agent/logs
Returns recent audit log entries for the authenticated agent.
curl "https://proxy.tap.human.tech/agent/logs?limit=5" \
-H "X-TAP-Key: $MY_KEY"Query parameters:
| Param | Default | Max | Description |
|---|---|---|---|
limit | 20 | 100 | Number of entries to return |
Response:
{
"agent_id": "my-agent",
"count": 1,
"entries": [
{
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "my-agent",
"credential_names": ["slack"],
"target_url": "https://slack.com/api/conversations.list",
"method": "GET",
"approval_status": "AutoApproved",
"upstream_status": 200,
"total_latency_ms": 145,
"approval_latency_ms": 0,
"upstream_latency_ms": 120,
"response_sanitized": false,
"timestamp": "2026-03-30T14:22:15.123Z"
}
]
}GET /agent/config
Returns the credential list for the authenticated agent.
curl https://proxy.tap.human.tech/agent/config \
-H "X-TAP-Key: $MY_KEY"Response:
{
"agent_id": "my-agent",
"credentials": [
{
"name": "slack",
"description": "Slack API",
"api_base": "https://slack.com/api"
}
]
}POST /agent/proposals
Propose a policy change for a human workspace manager to review. The proposal is inert — it never grants authority by itself; a manager approves or denies it in the dashboard (approval is passkey-gated). Capped at 20 pending proposals per agent (429 beyond that).
curl -X POST https://proxy.tap.human.tech/agent/proposals \
-H "X-TAP-Key: $MY_KEY" \
-H "Content-Type: application/json" \
-d '{
"proposal_type": "policy_change",
"payload": {
"credential_name": "slack",
"auto_approve_urls": ["/conversations.list"]
}
}'Poll the decision with GET /agent/proposals/{id} (team-scoped, same X-TAP-Key).
POST /agent/credential-link
Ask TAP for a prefilled dashboard link the agent can hand its user to create a new credential. The agent supplies metadata only (name, description, API base, allowed_hosts); any secret value in the payload is rejected with 400 — the human pastes the secret in the dashboard themselves.
curl -X POST https://proxy.tap.human.tech/agent/credential-link \
-H "X-TAP-Key: $MY_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "digitalocean", "description": "DigitalOcean PAT", "api_base": "https://api.digitalocean.com", "allowed_hosts": ["api.digitalocean.com"]}'Prefilling allowed_hosts opens the dashboard modal with the destination-host exfiltration binding already populated, so a secret-bearing credential is bound to its real upstream host rather than left unbound. It’s still metadata only — the human reviews and saves.
Response: {"create_url": "https://.../dashboard?prefill_credential=...#/credentials"}
When a /forward call references a credential that doesn’t exist, the error response also includes a credential_link_url so the agent can discover this flow.
GET /health
Health check endpoint. No authentication required.
curl https://proxy.tap.human.tech/healthReturns 200 OK with {"status": "ok", "build": {"sha": "...", "version": "..."}} when the proxy is running.
Admin Auth Endpoints
These endpoints handle team signup, email verification, login, and logout. No authentication is required for signup and login.
POST /signup
Create a new team and admin account.
Body:
{
"team_name": "my-team",
"email": "admin@example.com",
"password": "a-strong-password"
}Validation:
team_name: 3-64 characters, lowercase alphanumeric with hyphensemail: must contain@and.password: minimum 8 characters
Response (201):
{
"team_id": "550e8400-e29b-41d4-a716-446655440000",
"team_name": "my-team",
"admin_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Verification email sent. Check your inbox."
}curl example:
curl -X POST https://proxy.tap.human.tech/signup \
-H "Content-Type: application/json" \
-d '{"team_name": "my-team", "email": "admin@example.com", "password": "my-password"}'POST /verify-email
Verify email address with the 6-digit code sent during signup.
Body:
{
"email": "admin@example.com",
"code": "123456"
}Response (200):
{"verified": true}POST /login
Authenticate with email and password. Returns a session token valid for 24 hours.
Body:
{
"email": "admin@example.com",
"password": "my-password"
}Response (200):
{
"session_token": "a1b2c3d4e5f6...",
"admin_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"team_id": "550e8400-e29b-41d4-a716-446655440000",
"expires_at": "2026-04-03T14:00:00Z"
}curl example:
curl -X POST https://proxy.tap.human.tech/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "my-password"}'POST /logout
Invalidate the current session.
Header: Authorization: Bearer <session_token>
curl -X POST https://proxy.tap.human.tech/logout \
-H "Authorization: Bearer $SESSION_TOKEN"Response (200):
{"logged_out": true}Admin CRUD Endpoints
Dashboard/team management endpoints require Authorization: Bearer <session_token>. Operations are scoped to the active team in that session.
Credentials
Credential values are write-only. They are never returned by any API endpoint.
GET /team/credentials
List credentials for your team. Owners and admins see all credentials. Approvers see only credentials assigned to them on the Team page.
curl https://proxy.tap.human.tech/team/credentials \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"credentials": [
{
"name": "slack",
"description": "Slack API",
"connector": "direct",
"api_base": "https://slack.com/api",
"relative_target": false,
"auth_header_format": null,
"auth_bindings": [],
"value_hint": "xo***en"
}
]
}value_hint is a masked preview (first and last two characters) confirming a value is stored — the value itself is never returned.
POST /team/credentials
Create a new credential. Owners and admins only.
Body:
{
"name": "slack",
"description": "Slack API",
"connector": "direct",
"api_base": "https://slack.com/api",
"relative_target": false,
"auth_header_format": "Bearer {value}",
"auth_bindings": [],
"value": "xoxb-your-slack-token"
}| Field | Required | Default | Description |
|---|---|---|---|
name | yes | Credential identifier | |
description | yes | Human-readable description | |
connector | no | "direct" | "direct" (API key injection) or "sidecar" (external service) |
api_base | no | Base URL for the target API or sidecar | |
relative_target | no | false | If true, X-TAP-Target is a path appended to api_base |
auth_header_format | no | Format string for the Authorization header (e.g., "Bearer {value}") | |
auth_bindings | no | [] | Explicit auth header bindings. Each binding is { "header": "Header-Name", "format": "{value}" }. Use this for APIs that authenticate via non-Authorization headers. |
value | no | The secret credential value (write-only, never returned) |
auth_bindings is the recommended way to model APIs that use custom (non-Authorization) auth headers. When auth_bindings is set, TAP treats those headers as valid auth positions for placeholder substitution and injects them automatically in unified mode.
Multi-secret credentials (Datadog, and others)
APIs that need two or more independent secrets in one request are modeled as one credential whose value is a JSON object — value accepts either a string (single secret) or an object (multi-secret):
{
"name": "datadog",
"description": "Datadog API + application keys",
"connector": "direct",
"auth_bindings": [
{ "header": "DD-API-KEY", "format": "{value.api_key}" },
{ "header": "DD-APPLICATION-KEY", "format": "{value.app_key}" }
],
"value": { "api_key": "dd-api-key-here", "app_key": "dd-app-key-here" }
}With the bindings above, unified mode (X-TAP-Credential: datadog) injects both headers automatically. In placeholder mode, reference each field with the dotted form:
curl -X POST https://proxy.tap.human.tech/forward \
-H "X-TAP-Key: $MY_KEY" \
-H "X-TAP-Target: https://api.datadoghq.com/api/v1/validate" \
-H "X-TAP-Method: GET" \
-H "DD-API-KEY: <CREDENTIAL:datadog.api_key>" \
-H "DD-APPLICATION-KEY: <CREDENTIAL:datadog.app_key>"Field references (<CREDENTIAL:name.field>) may appear in any header — the agent is explicitly choosing the wiring — but body position rules still apply, and a bare <CREDENTIAL:name> against a multi-secret credential is never substituted. The dashboard equivalent is the Multi-secret option on a Custom credential — see Credential Setup.
curl example:
curl -X POST https://proxy.tap.human.tech/team/credentials \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "slack",
"description": "Slack API",
"connector": "direct",
"api_base": "https://slack.com/api",
"value": "xoxb-your-slack-token"
}'Response (201):
{"name": "slack", "created": true}DELETE /team/credentials/:name
Delete a credential.
curl -X DELETE https://proxy.tap.human.tech/team/credentials/slack \
-H "Authorization: Bearer $SESSION_TOKEN"Agents
GET /team/agents
List API keys. Owners and admins see all team API keys. Approvers see only API keys they created themselves.
curl https://proxy.tap.human.tech/team/agents \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"agents": [
{
"id": "research-bot",
"description": "Research assistant",
"enabled": true,
"rate_limit_per_hour": 100,
"created_at": "2026-04-01T10:00:00Z"
}
]
}POST /team/agents
Create an API key. The key is returned once and cannot be retrieved again. Owners and admins can assign roles and direct credentials. Approvers can create keys only for themselves, using direct credentials already assigned to them; approver-created keys cannot use roles.
Body:
{
"id": "research-bot",
"description": "Research assistant",
"roles": ["reader"],
"credentials": ["slack"],
"rate_limit_per_hour": 100
}| Field | Required | Description |
|---|---|---|
id | yes | Agent identifier |
description | no | Human-readable description |
roles | no | List of role names to assign. Owners/admins only |
credentials | no | List of credential names for direct access. Approvers may include only credentials assigned to them |
rate_limit_per_hour | no | Max requests per hour (null = unlimited) |
curl example:
curl -X POST https://proxy.tap.human.tech/team/agents \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "research-bot",
"description": "Research assistant",
"roles": ["reader"],
"credentials": ["slack"],
"rate_limit_per_hour": 100
}'Response (201):
{
"id": "research-bot",
"api_key": "a1b2c3d4e5f6...",
"message": "Save this API key — it will not be shown again."
}GET /team/agents/:id
Get agent details including effective credentials (union of role credentials and direct assignments).
curl https://proxy.tap.human.tech/team/agents/research-bot \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"id": "research-bot",
"description": "Research assistant",
"enabled": true,
"rate_limit_per_hour": 100,
"created_at": "2026-04-01T10:00:00Z",
"effective_credentials": ["github", "slack"]
}DELETE /team/agents/:id
curl -X DELETE https://proxy.tap.human.tech/team/agents/research-bot \
-H "Authorization: Bearer $SESSION_TOKEN"POST /team/agents/:id/enable
Re-enable a disabled agent.
curl -X POST https://proxy.tap.human.tech/team/agents/research-bot/enable \
-H "Authorization: Bearer $SESSION_TOKEN"POST /team/agents/:id/disable
Disable an agent. All requests from this agent will be rejected until re-enabled.
curl -X POST https://proxy.tap.human.tech/team/agents/research-bot/disable \
-H "Authorization: Bearer $SESSION_TOKEN"Roles
Roles provide RBAC for credential access. An agent’s effective permissions are the union of all its roles’ credentials plus its direct credential assignments.
GET /team/roles
curl https://proxy.tap.human.tech/team/roles \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"roles": [
{
"name": "reader",
"description": "Read-only API access",
"rate_limit_per_hour": 50
}
]
}POST /team/roles
Create a role with optional initial credentials. Owners and admins only.
Body:
{
"name": "reader",
"description": "Read-only API access",
"credentials": ["slack", "github"],
"rate_limit_per_hour": 50
}curl -X POST https://proxy.tap.human.tech/team/roles \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "reader", "description": "Read-only access", "credentials": ["slack", "github"]}'DELETE /team/roles/:name
Delete a role. Cascades — removes the role from all agents.
curl -X DELETE https://proxy.tap.human.tech/team/roles/reader \
-H "Authorization: Bearer $SESSION_TOKEN"Policies
Policies control per-credential approval behavior. See Policies & Approval for details on evaluation order.
GET /team/policies/:cred_name
curl https://proxy.tap.human.tech/team/policies/slack \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"credential": "slack",
"auto_approve_methods": ["GET", "HEAD"],
"require_approval_methods": ["POST", "PUT", "DELETE"],
"auto_approve_urls": ["/conversations.list", "/users.list"],
"allowed_approvers": ["alice@example.com"],
"approval_channel": "dashboard",
"telegram_chat_id": "-100123456789",
"matrix_room_id": null,
"matrix_allowed_approvers": [],
"require_passkey": false,
"min_approvals": 1
}Returns 404 if no policy is set for the credential.
PUT /team/policies/:cred_name
Set or update the policy for a credential.
Body:
{
"auto_approve_methods": ["GET", "HEAD"],
"require_approval_methods": ["POST", "PUT", "DELETE"],
"auto_approve_urls": ["/conversations.list"],
"allowed_approvers": ["alice@example.com"],
"approval_channel": "telegram",
"telegram_chat_id": "-100123456789",
"matrix_room_id": "!roomid:matrix.org",
"matrix_allowed_approvers": ["@user:matrix.org"],
"require_passkey": false,
"min_approvals": 1
}| Field | Description |
|---|---|
auto_approve_methods | HTTP methods that skip approval |
require_approval_methods | HTTP methods that require human approval |
auto_approve_urls | URL substrings that skip approval regardless of method |
allowed_approvers | Team member emails allowed to approve. Empty = anyone on the team |
approval_channel | Optional per-credential channel override: dashboard, agent_reflected, telegram, or matrix |
telegram_chat_id | Override the default Telegram chat for this credential |
matrix_room_id | Override the default Matrix room for this credential |
matrix_allowed_approvers | Matrix-specific approver list (@user:server format) |
require_passkey | Require WebAuthn passkey (Face ID / YubiKey) for approval |
min_approvals | Number of approvers who must approve before proceeding (default 1) |
curl example:
curl -X PUT https://proxy.tap.human.tech/team/policies/slack \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"auto_approve_methods": ["GET"],
"require_approval_methods": ["POST", "PUT", "DELETE"],
"auto_approve_urls": ["/conversations.list", "/users.list"]
}'Team
GET /team
Get your team information.
curl https://proxy.tap.human.tech/team \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "my-team",
"created_at": "2026-04-01T00:00:00Z"
}Notification Channels
Configure where approval requests are sent for your team.
GET /team/notification-channels
curl https://proxy.tap.human.tech/team/notification-channels \
-H "Authorization: Bearer $SESSION_TOKEN"Response:
{
"notification_channels": [
{
"id": "ch-1",
"channel_type": "telegram",
"name": "ops-channel",
"config": {"chat_id": "-100123456789"},
"enabled": true,
"created_at": "2026-04-01T00:00:00Z"
}
]
}POST /team/notification-channels
Create a notification channel. Supports telegram, matrix, dashboard, and agent_reflected.
dashboard and agent_reflected do not require external config:
{
"channel_type": "dashboard",
"name": "dashboard",
"config": {}
}{
"channel_type": "agent_reflected",
"name": "agent-reflected",
"config": {}
}Telegram:
{
"channel_type": "telegram",
"name": "ops-channel",
"config": {"chat_id": "-100123456789"}
}curl -X POST https://proxy.tap.human.tech/team/notification-channels \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"channel_type": "telegram", "name": "ops-channel", "config": {"chat_id": "-100123456789"}}'Matrix:
{
"channel_type": "matrix",
"name": "ops-matrix",
"config": {
"room_id": "!yourroom:matrix.org",
"homeserver_url": "https://matrix.org"
}
}homeserver_url is optional if the server has a Matrix bot configured with a default homeserver. Do not include access_token — that is a global secret managed by the server.
POST /team/notification-channels/:name/default
Make a channel the team default — it is tried first when routing approval requests.
curl -X POST https://proxy.tap.human.tech/team/notification-channels/ops-channel/default \
-H "Authorization: Bearer $SESSION_TOKEN"POST /team/notification-channels/:name/test
Send a test message through a channel to verify it’s wired up.
curl -X POST https://proxy.tap.human.tech/team/notification-channels/ops-channel/test \
-H "Authorization: Bearer $SESSION_TOKEN"DELETE /team/notification-channels/:name
curl -X DELETE https://proxy.tap.human.tech/team/notification-channels/ops-channel \
-H "Authorization: Bearer $SESSION_TOKEN"