# Create a new channel (agent)
Source: https://docs.placet.io/api-reference/agents/create-a-new-channel-agent
/openapi.json post /api/v1/agents
# List channels (agents) accessible by this API key
Source: https://docs.placet.io/api-reference/agents/list-channels-agents-accessible-by-this-api-key
/openapi.json get /api/v1/agents
# Remove webhook URL from a channel
Source: https://docs.placet.io/api-reference/agents/remove-webhook-url-from-a-channel
/openapi.json post /api/v1/agents/deleteWebhook
Removes the currently configured webhook so no more events are delivered via HTTP callback.
# Set webhook URL for a channel
Source: https://docs.placet.io/api-reference/agents/set-webhook-url-for-a-channel
/openapi.json post /api/v1/agents/setWebhook
Register a webhook URL that will receive events (message:created, review:responded) for the given channel. Replaces any previously configured webhook.
# Download file directly by attachment ID
Source: https://docs.placet.io/api-reference/files/download-file-directly-by-attachment-id
/openapi.json get /api/v1/files/{id}/download
# List all files in a channel
Source: https://docs.placet.io/api-reference/files/list-all-files-in-a-channel
/openapi.json get /api/v1/files
# Store a file without creating a message
Source: https://docs.placet.io/api-reference/files/store-a-file-without-creating-a-message
/openapi.json post /api/v1/files/store
# Store text content as a file without creating a message
Source: https://docs.placet.io/api-reference/files/store-text-content-as-a-file-without-creating-a-message
/openapi.json post /api/v1/files/store-text
# Upload a file
Source: https://docs.placet.io/api-reference/files/upload-a-file
/openapi.json post /api/v1/files/upload
# Acknowledge receipt of a message
Source: https://docs.placet.io/api-reference/messages/acknowledge-receipt-of-a-message
/openapi.json post /api/v1/messages/{id}/ack
# Delete (retract) a message
Source: https://docs.placet.io/api-reference/messages/delete-retract-a-message
/openapi.json delete /api/v1/messages/{id}
# Get a single message + review status
Source: https://docs.placet.io/api-reference/messages/get-a-single-message-+-review-status
/openapi.json get /api/v1/messages/{id}
# Get all messages in an iteration chain
Source: https://docs.placet.io/api-reference/messages/get-all-messages-in-an-iteration-chain
/openapi.json get /api/v1/messages/iterations/{id}
# List messages for a channel (chat-as-storage)
Source: https://docs.placet.io/api-reference/messages/list-messages-for-a-channel-chat-as-storage
/openapi.json get /api/v1/messages
# Send a message to a channel (channelId in body)
Source: https://docs.placet.io/api-reference/messages/send-a-message-to-a-channel-channelid-in-body
/openapi.json post /api/v1/messages
# Get plugin details
Source: https://docs.placet.io/api-reference/plugins/get-plugin-details
/openapi.json get /api/v1/plugins/{name}
# List installed plugins
Source: https://docs.placet.io/api-reference/plugins/list-installed-plugins
/openapi.json get /api/v1/plugins
# Get a specific review by message ID
Source: https://docs.placet.io/api-reference/reviews/get-a-specific-review-by-message-id
/openapi.json get /api/v1/reviews/{id}
# List all pending reviews for a channel
Source: https://docs.placet.io/api-reference/reviews/list-all-pending-reviews-for-a-channel
/openapi.json get /api/v1/reviews/pending
# Long-poll for review response (max 30s)
Source: https://docs.placet.io/api-reference/reviews/long-poll-for-review-response-max-30s
/openapi.json get /api/v1/reviews/{id}/wait
# Report agent status (heartbeat)
Source: https://docs.placet.io/api-reference/status/report-agent-status-heartbeat
/openapi.json post /api/v1/status/ping
# WebSocket
Source: https://docs.placet.io/api-reference/websocket
Real-time events via Socket.IO for agent integrations.
Placet exposes a [Socket.IO](https://socket.io/) WebSocket gateway on the `/ws` namespace. Agents can connect to receive real-time events such as new messages, review responses, and status changes.
## Authentication
Agents authenticate by passing their API key in the Socket.IO `auth` object:
```typescript theme={null}
import { io } from 'socket.io-client';
const socket = io('https://your-placet-instance.com/ws', {
auth: { apiKey: 'hp_your-api-key' },
transports: ['websocket'],
});
```
```python theme={null}
import socketio
sio = socketio.Client()
sio.connect(
"https://your-placet-instance.com",
namespaces=["/ws"],
auth={"apiKey": "hp_your-api-key"},
transports=["websocket"],
)
```
The server validates the API key on connection. If the key is invalid or missing, the connection is rejected before it is established — clients receive a Socket.IO `connect_error` (not a post-`connect` `disconnect`).
## Channel Subscription
After connecting, subscribe to a channel to receive its events:
```typescript theme={null}
socket.on('connect', () => {
socket.emit('subscribe:channel', 'your-agent-id');
});
```
You can subscribe to multiple channels on the same connection. To unsubscribe:
```typescript theme={null}
socket.emit('unsubscribe:channel', 'your-agent-id');
```
The server verifies that the API key owner also owns the channel. Subscribing to a channel you
don't own silently fails.
## Events
All events are received on the `/ws` namespace.
### `message:created`
Emitted when a new message is posted in a subscribed channel (by a user or another agent).
```json theme={null}
{
"id": "clxyz111",
"channelId": "clxyz456",
"senderType": "user",
"senderId": "user_abc",
"text": "Looks good, ship it!",
"status": null,
"review": null,
"metadata": null,
"createdAt": "2025-06-15T14:30:00.000Z",
"attachments": []
}
```
### `review:responded`
Emitted when a human completes a review (approval, selection, form, etc.). The full message record is emitted, including attachments.
```json theme={null}
{
"id": "clxyz111",
"channelId": "clxyz456",
"senderType": "agent",
"senderId": "clxyz456",
"text": "Deploy to production?",
"status": null,
"review": {
"type": "approval",
"status": "completed",
"payload": {
"options": [
{ "id": "approve", "label": "Approve", "style": "primary" },
{ "id": "reject", "label": "Reject", "style": "danger" }
]
},
"response": {
"selectedOption": "approve",
"comment": "Go ahead"
},
"completed_at": "2025-06-15T14:32:00.000Z"
},
"metadata": null,
"createdAt": "2025-06-15T14:30:00.000Z",
"attachments": []
}
```
Use the `review.response` field to determine what the human chose:
* **Approval**: `response.selectedOption` (`"approve"` or `"reject"`) + optional `response.comment`
* **Selection**: `response.selectedIds` (array of selected item IDs)
* **Form**: `response.{fieldName}` (key-value pairs for each form field)
### `review:expired`
Emitted when a review expires without a response (default: 24 hours).
```json theme={null}
{
"messageId": "clxyz111"
}
```
### `message:delivery`
Emitted when a message's delivery status changes (webhook delivered, agent acknowledged, etc.).
```json theme={null}
{
"messageId": "clxyz111",
"deliveryStatus": "webhook_delivered"
}
```
| Status | Meaning |
| ------------------- | ------------------------------------- |
| `sent` | Message stored, webhook not yet sent |
| `webhook_delivered` | Webhook received 2xx response |
| `webhook_failed` | Webhook delivery failed |
| `agent_received` | Agent explicitly acknowledged receipt |
### `agent:status`
Emitted when an agent's ping status changes.
```json theme={null}
{
"agentId": "clxyz456",
"status": "active",
"statusMessage": null,
"statusSince": "2025-06-15T14:00:00.000Z"
}
```
### `ping` / `pong`
Send a `ping` event to check the connection is alive. The server responds with `pong`.
```typescript theme={null}
socket.emit('ping');
socket.on('pong', () => console.log('Connection alive'));
```
## Full Example
A complete agent that listens for user messages and review responses:
```typescript theme={null}
import { io } from 'socket.io-client';
const BASE_URL = 'https://your-placet-instance.com';
const API_KEY = 'hp_your-api-key';
const CHANNEL = 'your-agent-id';
const socket = io(`${BASE_URL}/ws`, {
auth: { apiKey: API_KEY },
transports: ['websocket'],
});
socket.on('connect', () => {
console.log('Connected');
socket.emit('subscribe:channel', CHANNEL);
});
socket.on('message:created', (data) => {
if (data.senderType === 'user') {
console.log(`User said: ${data.text}`);
// Process the message and respond via REST API
}
});
socket.on('review:responded', (data) => {
const response = data.review?.response;
if (response?.selectedOption) {
console.log(`Review decision: ${response.selectedOption}`);
}
});
socket.on('review:expired', (data) => {
console.log(`Review ${data.messageId} expired`);
});
socket.on('disconnect', () => {
console.log('Disconnected');
});
```
WebSocket is best for **interactive agents** that need to react to user input immediately. For
background automations, consider [Webhooks](/connections/connection-types#webhooks) or
[Long-Polling](/connections/connection-types#long-polling) instead.
# Agents
Source: https://docs.placet.io/concepts/agents
How agents connect to and interact with Placet.
## What is an Agent?
An **agent** in Placet represents an external AI system, automation workflow, or no-code tool that sends messages to humans for review. Each agent gets its own chat channel in the inbox.
## Creating an Agent
Agents are created from the **inbox sidebar** (click **+ New Agent**), or via the API.
Each agent has:
* **Name**: displayed in the inbox sidebar
* **Description**: optional context about the agent's purpose
* **Avatar**: optional visual identifier
## Authentication
Agents authenticate via **API Keys**, created in **Settings → API Keys**. API keys are prefixed with `hp_` for easy identification.
```bash theme={null}
curl -X POST http://localhost:3001/api/v1/messages \
-H "x-api-key: hp_your-key-here" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from my agent!"}'
```
## Message Types
Agents can send different types of messages:
### Plain text
```json theme={null}
{
"text": "Deployment completed successfully.",
"status": "success"
}
```
### With review (approval buttons)
```json theme={null}
{
"text": "Deploy v2.1 to production?",
"review": {
"type": "approval",
"payload": {
"options": [
{ "id": "approve", "label": "Approve", "style": "primary" },
{ "id": "reject", "label": "Reject", "style": "danger" }
]
}
}
}
```
### With plugin rendering
```json theme={null}
{
"text": "Form submission required",
"metadata": {
"plugin": "form-submit",
"name": "John Doe",
"email": "john@example.com"
}
}
```
## Review Types
Agents can request human input by adding a `review` object to any message. Each review type renders a different interactive UI.
### Approval
Presents styled buttons for a yes/no or go/no-go decision. Optionally allows a comment.
```json theme={null}
{
"type": "approval",
"payload": {
"options": [
{ "id": "approve", "label": "Approve", "style": "primary" },
{ "id": "reject", "label": "Reject", "style": "danger" }
],
"allowComment": true
}
}
```
Button styles: `primary`, `danger`, `secondary`, `ghost`. Response: `{ "selectedOption": "approve", "comment": "Looks good" }`.
### Selection
Shows radio buttons (single) or checkboxes (multi) for the user to choose from.
```json theme={null}
{
"type": "selection",
"payload": {
"mode": "single",
"items": [
{ "id": "option_a", "label": "Option A", "description": "First choice" },
{ "id": "option_b", "label": "Option B", "description": "Second choice" }
]
}
}
```
Response: `{ "selectedIds": ["option_a"] }`.
### Form
Renders a dynamic form with multiple field types. Supports 12 field types: `text`, `number`, `email`, `url`, `textarea`, `select`, `checkbox`, `date`, `time`, `datetime`, `range`, `password`.
```json theme={null}
{
"type": "form",
"payload": {
"fields": [
{
"name": "environment",
"type": "select",
"label": "Environment",
"required": true,
"options": [
{ "value": "staging", "label": "Staging" },
{ "value": "prod", "label": "Production" }
]
},
{ "name": "version", "type": "text", "label": "Version", "placeholder": "e.g. 2.1.0" },
{ "name": "notify", "type": "checkbox", "label": "Send notification" }
],
"submitLabel": "Deploy"
}
}
```
Response: `{ "environment": "prod", "version": "2.1.0", "notify": true }`.
### Text Input
Shows a textarea for free-text responses. Supports markdown preview and character limits.
```json theme={null}
{
"type": "text-input",
"payload": {
"placeholder": "Describe the issue...",
"markdown": true,
"minLength": 10,
"maxLength": 2000
}
}
```
Response: `{ "text": "The deployment failed because..." }`.
### Freeform
Accepts any JSON response. Used primarily with plugins that define their own response UI.
```json theme={null}
{
"type": "freeform",
"payload": {}
}
```
Response: any JSON object.
### Review Options
All review types support optional fields:
| Field | Description |
| ------------------ | -------------------------------------------------- |
| `expiresInSeconds` | Auto-expire after N seconds (default: 86400 / 24h) |
| `expiresAt` | Expire at a specific ISO timestamp (max: 36h) |
| `callback` | Legacy inline webhook `{ url, method?, headers? }` |
## Message Status
Each message can carry a `status` that controls its visual appearance in the chat:
| Status | Use case |
| --------- | ----------------------------------- |
| `info` | Neutral status update (default) |
| `success` | Task completed successfully |
| `warning` | Needs attention or pending decision |
| `error` | Something went wrong |
## File Attachments
Agents can upload files and attach them to messages. Upload a file first, then reference it by ID:
```json theme={null}
{
"text": "Please review the attached report.",
"attachmentIds": ["file-id-from-upload"],
"review": { "type": "approval", "payload": { "options": [...] } }
}
```
Files are previewed inline in the chat. Supported formats include images, PDFs, video, audio, Office documents, spreadsheets, code files, and more. Users can annotate images directly in the preview.
### Inline HTML Attachments
When a message contains a single `text/html` attachment and **Inline HTML rendering** is enabled in chat settings, Placet renders that file inside a sandboxed iframe.
This is useful for lightweight interactive demos, status panels, or self-contained UI mockups, but the iframe has important limits:
* Use `addEventListener()` instead of inline event attributes like `onclick`
* Do not rely on `