PingHuman API
Human in the Loop
Magic Link
HMAC Callback

Agent stuck? Ping a human.

POST an escalation, notify Slack or email with a single-use reply link, then poll or receive a signed callback. Context stays on the reply page. It is never in the notification.

pending · to resolvedSlack/email · channelsHMAC · callbacksOwner · scoped poll

Inspect the escalation contract.

PingHuman creates real human review escalations. This playground is a static sample of POST /escalations so the public site cannot notify Slack or email. Copy the curl into your own environment with an API key when you want a live escalation.

Request builder
Static sample. No live gateway call.
POST /escalations (static)
{
  "title": "Approve refund over $500?",
  "context": "Customer #1042 requests a $750 refund. Order history attached.",
  "options": [
    {
      "id": "approve",
      "label": "Approve"
    },
    {
      "id": "deny",
      "label": "Deny"
    }
  ],
  "fields": [
    {
      "name": "note",
      "type": "text",
      "label": "Note"
    }
  ],
  "channels": [
    {
      "type": "email",
      "to": "ops@example.com"
    }
  ],
  "ttl_seconds": 7200
}

Run replays the documented success envelope. It does not hit /api/internal/ping-human.

Output preview
Live response from the API, plus the exact request to reproduce it.
Approve refund over $500?
esc_8f4b1c2d · poll every 5s
pending

Static contract preview. The public playground never calls the gateway and never opens a live escalation.

Why PingHuman

Human verdicts for stuck agents.

Agent escalations

POST title, context, optional choices, and 1 to 3 notification channels. Get poll_url back.

Slack and email notify

Notifications carry title plus a magic link. Private context never leaves the reply page.

Single-use reply tokens

256-bit capabilities stored only as SHA-256 hashes. Expired links return 410. Races return 409.

Poll or callback

GET the owner-scoped poll URL, or supply a callback for HMAC-signed resolve and expiry events.

Signed deliveries

x-pinghuman-signature is hex HMAC-SHA256 of the body. Retries fire immediately, then +30s and +5min.

Owner-scoped reads

Unknown IDs and non-owners both get 404. Slack webhooks must be hooks.slack.com. Callbacks are SSRF-guarded.

/api/v1/ping-human

Endpoints, at a glance.

Create, poll, and cancel are owner-scoped. The public reply page is a single-use capability at /r/:token, not an API-key route.
POST/api/v1/ping-human/escalations

Create escalation

Notify Slack or email with a magic link. Returns 201 with poll_url and poll_interval_seconds.

PRIMARY
GET/api/v1/ping-human/escalations/:id

Poll status

Owner-scoped. Cache-Control no-store. Includes the human response when resolved.

ParameterTypeRequiredDescription
titlestringyes1 to 200 characters. Shown in notifications.
contextstringyes1 to 8000 characters. Reply page only. Always escaped.
optionsarrayno2 to 6 {id, label} choices. Omit for a plain acknowledge.
fieldsarraynoUp to 5 {name, type: text|number, label?, required?} inputs.
channelsarrayyes1 to 3 slack webhook_url (hooks.slack.com) or email to.
ttl_secondsintegernoLifetime 60 to 86400. Default 3600.
callbackobjectno{url, secret 16-128}. URL is SSRF-guarded. Secret encrypted at rest.

DELETE /api/v1/ping-human/escalations/:id cancels a pending or notified escalation. GET/POST /reply/:token is the public HTML reply surface.

Error reference

Failures you can plan for.

JSON errors use error.code. Reply pages return HTML with a locked-down CSP.
400Validation Error

Invalid title, context, channels, Slack host, or an SSRF-blocked callback URL.

401Authentication Error

Missing gateway identity or invalid API key. Send x-api-key or api_key with agent routes.

404Not Found

Unknown escalation, not the owner, unknown reply token, or unknown path. Lookups do not leak existence.

409Conflict Error

Already resolved or cancelled, or another caller won the single-use reply race.

410Expired Error

The magic-link reply token expired. The reply page renders HTML, not JSON.

429Rate Limit Error

Create 60, poll 1200, or reply 10 per 15 minutes. retryAfter is in seconds.

503Callback Unavailable

A callback was supplied but PINGHUMAN_ENC_KEY is not configured.

Example error response

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Not found"
  },
  "correlationId": "req_error123"
}

FAQ

API questions developers ask first.

How does an agent get an answer?

POST /escalations returns poll_url and poll_interval_seconds. GET that URL until status is resolved, expired, or cancelled, or supply a callback URL for an HMAC-signed webhook.

What is sent to Slack or email?

Notifications include the title and the reply link only. The private context field is rendered on the reply page and is always escaped. It never leaves that page.

Can another account poll my escalation?

No. Escalations are owner-scoped. Unknown IDs and non-owners both receive 404 with no existence leak. Reply tokens are 256-bit capabilities stored only as SHA-256 hashes.

How do I verify a callback?

Compare x-pinghuman-signature to hex HMAC-SHA256 of the JSON body using your callback secret. Also check x-pinghuman-timestamp within a 5 minute window. Retries fire immediately, then at +30s and +5min.

How do I authenticate requests?

Send your Datpaq API key as an x-api-key header or an api_key query parameter. Keys are created in the Datpaq dashboard and a free tier is included. The public reply page is a single-use capability, not an API-key route.

Give agents a human panic button.

POST an escalation, notify Slack or email with a magic link, then poll or receive an HMAC-signed callback when a person decides.