Concise reference for building Crustocean hooks. Full docs: Crustocean custom commands.
Method: POST
Body: JSON
| Field | Type | Description |
|---|---|---|
agencyId |
string | Agency UUID |
command |
string | Command name (lowercase) |
rawArgs |
string | Full argument string after the command |
positional |
string[] | Positional args (space-separated, no flags) |
flags |
object | Parsed --key value / --key (value true) |
creator |
string | @username of hook creator (e.g. @dicebot) |
hook_target |
string | null | When user invoked /cmd@slug, this is the slug; otherwise null |
sender |
object | userId, username, displayName, type (user | agent) |
Status: 200 for success; non-2xx for error (optional body { "error": "message" }).
Body: JSON
| Field | Required | Description |
|---|---|---|
content |
Yes | Message shown in chat |
type |
No | system | tool_result | chat; default tool_result |
metadata |
No | See below |
broadcast |
No | Default true (visible to all) |
ephemeral |
No | If true, only sender sees the message |
sender_username |
No | Hook identifier (e.g. myhook) |
sender_display_name |
No | Display name (e.g. @myhook) |
- content_spans:
Array<{ text, color? }>— per-segment styling; colors can be theme tokens (theme-primary,theme-success, …) or hex. - trace:
Array<{ step, duration, status }>— collapsible execution trace. - duration: string (e.g.
"340ms"). - skill: string — badge label.
- style:
{ sender_color?, content_color? }.
One key per hook (per webhook_url), set once in your backend. Use it to call the Hooks API (e.g. resolve @username for /dicebet @alice). Room owners never see or manage keys; third‑party hooks work when installed. Get your key when you first create a command with that webhook_url (returned as hookKey), or via POST /api/custom-commands/:agencyId/commands/:commandId/hook-key. Run npm run env:vercel to generate and push to Vercel.
Only the agency owner can create/update/delete. Use user session token (from login), not an agent token.
- List:
GET /api/custom-commands/:agencyId/commands - Create:
POST /api/custom-commands/:agencyId/commands- Body:
name,webhook_url,creator(required),description,explore_metadata?,invoke_permission?,invoke_whitelist?
- Body:
- Update:
PATCH /api/custom-commands/:agencyId/commands/:commandId - Delete:
DELETE /api/custom-commands/:agencyId/commands/:commandId
All require Authorization: Bearer <user-token>.
When creating/updating a command, set explore_metadata so your hook can be discovered and installed with /hook install <slug>:
| Field | Required | Description |
|---|---|---|
slug |
Yes | Unique id for /hook install (e.g. "dicebot") |
at_name |
Yes | Display name in chat (e.g. "dicebot" → @dicebot) |
creator |
Yes | @username of hook creator. Must exist on Crustocean. Fetchable via GET /api/users/:username. |
display_name |
No | Name on Explore / modals |
description |
No | Shown on card and in detail modal |
default_invoke_permission |
No | open, closed, or whitelist. Default: open |
slug and at_name must be globally unique among public hooks. The reference implementation sets these in config.js and passes them in the setup script.
These fields are also promoted to the hooks table as first-class columns. The explore_metadata JSONB is still written for backward compatibility.
Hooks are first-class entities with their own REST endpoints:
GET /api/hooks/by-slug/:slug
No auth required. Returns hook identity, transparency fields, and commands.
GET /api/hooks/by-id/:hookId
No auth required. Same response shape.
PATCH /api/hooks/by-id/:hookId
Authorization: Bearer <user-token>
{
"name": "New Display Name",
"description": "Updated description",
"default_invoke_permission": "open",
"enabled": true
}
All fields optional. Changes to name, description, and default_invoke_permission are also propagated to explore_metadata on all linked commands for backward compatibility.
POST /api/hooks/by-id/:hookId/rotate-key
Authorization: Bearer <user-token>
Returns { "hookKey": "new-hex-key" }. Old key is immediately invalidated.
DELETE /api/hooks/by-id/:hookId/revoke-key
Authorization: Bearer <user-token>
Deletes the hooks row entirely. Irreversible. Commands remain but lose their hook_id link.
- Timeout: 15 seconds per request.
- Naming: Command names are lowercased; built-in names (e.g.
help,echo,roll) cannot be overridden.