Skip to main content
Placet supports three patterns for delivering human responses back to agents. You can use them independently or combine them.

Comparison

FeatureWebSocketWebhookLong-Polling
LatencyReal-timeNear real-timeUp to 30s
DirectionBidirectionalPush to agentAgent pulls
Persistent connectionYesNoNo
Firewall-friendlyRequires WS supportAgent needs public URLYes
Best forInteractive agentsBackground automationsSimple scripts

WebSocket

Real-time bidirectional communication via Socket.IO on the /ws namespace.

How it works

Events

EventDirectionDescription
message:createdServer → ClientNew message in a subscribed channel
review:respondedServer → ClientHuman completed a review
review:expiredServer → ClientReview expired (default: 24h)
agent:statusServer → ClientAgent ping status changed

Connection Example

import { io } from 'socket.io-client';

const socket = io('https://your-placet-instance.com/ws', {
  auth: { token: 'your-jwt-token' },
});

socket.on('connect', () => {
  console.log('Connected to Placet WebSocket');
});

socket.on('review:responded', (data) => {
  console.log(`Review ${data.id} answered: ${data.review.response.selectedOption}`);
});

socket.on('message:created', (data) => {
  console.log(`New message: ${data.text}`);
});
WebSocket connections use JWT authentication (not API keys). This is primarily used by the frontend. For agent integrations, prefer webhooks or long-polling with API keys.

Webhooks

Receive HTTP callbacks when a human responds. No persistent connection required, Placet pushes to your endpoint.

How it works

Webhook Priority

Placet dispatches webhooks in a 3-tier priority order:
  1. Message-level webhookUrl: set per message in the request body
  2. Agent-level webhookUrl: configured on the agent in settings
  3. Legacy callback: fallback for backward compatibility
The first matching webhook URL is used. If multiple tiers are configured, higher priority wins.

Setting a Webhook

Per message:
curl -X POST "$PLACET_URL/api/v1/messages" \
  -H "Authorization: Bearer $PLACET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channelId": "your-agent-id",
    "text": "Approve deployment?",
    "webhookUrl": "https://your-server.com/webhook",
    "review": {
      "type": "approval",
      "payload": {
        "options": [
          {"id": "approve", "label": "Approve", "style": "primary"},
          {"id": "reject", "label": "Reject", "style": "danger"}
        ]
      }
    }
  }'

Webhook Security

  • SSRF protection: Placet validates webhook URLs against an allowlist and blocks private/internal IPs
  • Timeout: Webhook requests have a 10-second timeout
  • Retries: Failed deliveries are not retried (design your agent to handle missed webhooks)
Your webhook endpoint must be publicly accessible. Placet blocks requests to private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x) to prevent SSRF attacks.

Long-Polling

The simplest pattern: your agent sends a request and waits for a response. No server infrastructure needed.

How it works

Usage

# 1. Send a message with a review
RESPONSE=$(curl -s -X POST "$PLACET_URL/api/v1/messages" \
  -H "Authorization: Bearer $PLACET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channelId": "your-agent-id",
    "text": "Continue processing?",
    "review": {
      "type": "approval",
      "payload": {
        "options": [
          {"id": "yes", "label": "Yes"},
          {"id": "no", "label": "No"}
        ]
      }
    }
  }')

MESSAGE_ID=$(echo "$RESPONSE" | jq -r '.id')

# 2. Long-poll for the response
curl -s "$PLACET_URL/api/v1/reviews/$MESSAGE_ID/wait?channel=your-agent-id&timeout=30000" \
  -H "Authorization: Bearer $PLACET_API_KEY"

Parameters

ParameterDefaultDescription
channelrequiredAgent/channel ID
timeout30000Max wait time in ms (capped at 30000)

Response States

statusMeaning
completedHuman responded, response field contains the answer
pendingTimeout reached, no response yet, retry the poll
expiredReview expired (default 24h), no response will come
If the first poll returns pending, simply call the endpoint again. The review stays active until a human responds or it expires (default 24 hours, max 36 hours).

Choosing a Pattern

Use Long-Polling with the /api/v1/reviews/{id}/wait endpoint. It’s the simplest to implement and works from any environment. Send a message, poll for the response, then continue your logic.
Use Webhooks. Set a webhookUrl on each message and let Placet push responses to your server. This way your automation doesn’t block while waiting.
Use WebSocket. Connect via Socket.IO and subscribe to real-time events. This is how the Placet frontend itself works.
Use fire-and-forget: simply POST /api/v1/messages without a review field. No need for any response mechanism. Users still receive push notifications and see the message in real-time.

Push Notifications

In addition to the three agent-facing delivery patterns above, Placet sends browser push notifications to users when new messages arrive. This works independently of which pattern the agent uses: users receive a notification even when the browser tab is in the background or closed. Push notifications are powered by the Web Push API (VAPID) and can be enabled per user in Settings > Appearance.