API reference
Base URL https://app.herald.run; versioned endpoints live under /api/v1. Send the connection token as Authorization: Bearer hdr_…; your identity and account come from the token, never the request body. JSON bodies are limited to 128 KiB.
The machine-readable description is openapi.json (OpenAPI 3.1), generated from the same schemas the server validates with. Point a client generator or API tool at it.
Scopes
| Scope | Grants |
|---|---|
messages:send |
Send, cancel, run tasks, deliver secrets |
messages:read |
Read messages, threads, logs, files; collect secrets |
destinations:manage |
Register and manage destinations |
events:read |
The live socket |
files:write |
Upload files |
Endpoints marked human need a signed-in app session and reject connection tokens.
Account
| Endpoint | Access | Body | Response |
|---|---|---|---|
GET /api/pingLiveness and server version | public | 200 Ping | |
GET /api/v1/tokenThe calling token, its scopes and account | any token | 200 TokenInfo | |
DELETE /api/v1/tokenRevoke the calling token | any token | 204 | |
GET /api/v1/accountPlan, usage and reset date | any token | 200 Account | |
PATCH /api/v1/accountRename the account | human | { name } | 200 Account |
DELETE /api/v1/accountDelete the account and all its data | human | { confirm } | 204 |
GET /api/v1/account/exportDownload every account record as JSON | human | 200 { } |
Messages
| Endpoint | Access | Body | Response |
|---|---|---|---|
POST /api/v1/messagesSend a message, request or form | messages:send | SealedMessageInput | 201 Message |
GET /api/v1/messagesList messages (newest first) or the revision feed | messages:read | 200 Message | Tombstone[] | |
GET /api/v1/messages/{id}A full message or task | messages:read | 200 Message | |
PATCH /api/v1/messages/{id}Human: mark read | human | { read } | 200 { ok } |
DELETE /api/v1/messages/{id}Human: delete content and unshared files | human | 200 { ok } | |
POST /api/v1/messages/{id}/cancelCancel a pending request | messages:send | 200 Message | |
GET /api/v1/messages/{id}/threadThread messages, oldest first, 100 per page | messages:read | 200 { messages, next } | |
GET /api/v1/messages/{id}/logsTask logs | messages:read | 200 { logs } | |
POST /api/v1/messages/{id}/responseAnswer a request once | human | SealedResponseInput | 200 Message |
POST /api/v1/messages/{id}/secretsCollect sealed secret answers once (15 min) | messages:read | 200 object | |
GET /api/v1/messages/{id}/secret-receiverSender: the human device waiting for a secret | messages:read | 200 { claimId, publicKey, sig, received } | |
POST /api/v1/messages/{id}/secret-receiverHuman: claim a secret offer with a device key | human | { claimId, publicKey, sig } | 200 { ok } |
POST /api/v1/messages/{id}/secret-deliverySender: deliver the encrypted secret | messages:send | { claimId, envelope } | 200 { ok } |
POST /api/v1/messages/{id}/secret-receiveHuman: consume the delivered secret | human | { claimId } | 200 { envelope, pending } |
POST /api/v1/messages/syncHuman: current records for up to 100 IDs | human | { ids } | 200 Message[] |
Tasks
| Endpoint | Access | Body | Response |
|---|---|---|---|
POST /api/v1/outboxHuman: dispatch a task to a destination | human | SealedOutboxInput | 201 Message |
POST /api/v1/tasks/{id}/claimPull runner: claim a queued task | messages:send | { runnerId, heartbeat } | 200 { lease, leaseExpiresAt, task } |
POST /api/v1/tasks/{id}/heartbeatPull runner: renew the lease (every 60 s) | messages:send | { lease } | 200 { lease, leaseExpiresAt } |
POST /api/v1/tasks/{id}/updateRunner: append logs, complete or fail | messages:send | { lease, status, errorSealed, logs } | 200 Message |
Destinations
| Endpoint | Access | Body | Response |
|---|---|---|---|
GET /api/v1/destinationsList destinations | destinations:manage | 200 Destination[] | |
POST /api/v1/destinationsRegister a destination; a repeated key updates it | destinations:manage | DestinationInput | 201 Destination |
PUT /api/v1/destinations/{id}Replace a destination | destinations:manage | DestinationInput | 200 Destination |
DELETE /api/v1/destinations/{id}Remove a destination and cancel its queued tasks | destinations:manage | 200 { ok } | |
POST /api/v1/destinations/{id}/secretRotate a webhook signing secret (returned once) | destinations:manage | 200 Destination |
Files
| Endpoint | Access | Body | Response |
|---|---|---|---|
POST /api/v1/filesReserve an upload (≤100 MB, 15 min) | files:write | { name, type, size } | 201 Attachment |
PUT /api/v1/files/{id}Upload the exact reserved bytes once | files:write | bytes | 200 Attachment |
GET /api/v1/files/{id}Download a file | messages:read | 200 |
Connections
| Endpoint | Access | Body | Response |
|---|---|---|---|
GET /api/v1/integrationsList connections | any token | 200 Integration[] | |
POST /api/v1/integrationsCreate a connection; the token is returned once | human | { name, scopes, certificate } | 201 { token, integration } |
PATCH /api/v1/integrations/{id}Rename or recolor a connection | human | { name, color } | 200 Integration |
DELETE /api/v1/integrations/{id}Revoke a connection and its destinations | human | 200 { ok } | |
POST /api/v1/ws-ticketOne-time socket ticket (30 s) | human | 200 { ticket } |
Encryption
| Endpoint | Access | Body | Response |
|---|---|---|---|
GET /api/v1/keysThe account statement and connection certificates | messages:read | 200 KeyDirectory | |
PUT /api/v1/keys/accountPublish the account statement (201 new, 200 same identity) | human | AccountKeys | 201 AccountKeys |
DELETE /api/v1/keys/accountTurn end-to-end encryption off and delete every key record | human | { confirm } | 204 |
GET /api/v1/keys/backupsWrapped keyring backups | human | 200 { backups } | |
PUT /api/v1/keys/backups/{id}Store a wrapped keyring (at most 10) | human | { wrapped } | 200 KeyBackup |
DELETE /api/v1/keys/backups/{id}Delete a backup | human | 204 | |
PUT /api/v1/keys/device-code/{code}Certify CLI keys for a device user code (15 min) | human | Certificate | 200 Certificate |
GET /api/v1/keys/device-code/{code}CLI: fetch its certificate once | human | 200 Certificate | |
POST /api/v1/keys/devicesLocked device: ask another device for the keyring (15 min) | human | { label, commit } | 201 PendingDevice |
GET /api/v1/keys/devicesDevices waiting for approval | human | 200 { devices } | |
GET /api/v1/keys/devices/{id}A pending device; the envelope once approved | human | 200 PendingDevice | |
DELETE /api/v1/keys/devices/{id}Reject a pending device; again to remove it | human | 204 | |
POST /api/v1/keys/devices/{id}/challengeApprover: send a nonce (waiting → challenged) | human | { nonce } | 200 PendingDevice |
POST /api/v1/keys/devices/{id}/revealLocked device: open the commitment (challenged → revealed) | human | { transfer, nonce } | 200 PendingDevice |
POST /api/v1/keys/devices/{id}/approveApprover: send the keyring envelope (revealed → approved) | human | { envelope } | 200 PendingDevice |
Schemas
MessageInput
| Field | Type |
|---|---|
title required | string |
body | string |
runId | string |
checkpointId | string |
taskId | string |
replyTo | string |
requiresReview | boolean |
group | string |
expiresAt | string |
expiresIn | number |
kind | "message" | "form" | "report" | "files" |
fields | Field[] |
schema | object |
questions | { question, header, options, multiSelect }[] |
blocks | ReviewBlock[] |
actions | Action[] |
attachments | Attachment[] |
publicKey | PublicKey |
or
| Field | Type |
|---|---|
title required | string |
body | string |
runId | string |
checkpointId | string |
taskId | string |
replyTo | string |
requiresReview | boolean |
group | string |
expiresAt | string |
expiresIn | number |
kind required | "secret" |
fields | any |
attachments | any |
schema | any |
questions | any |
blocks | any |
actions | any |
publicKey | any |
Field
| Field | Type |
|---|---|
id required | string |
label required | string |
header | string |
type required | "text" | "textarea" | "number" | "select" | "multiselect" | "boolean" | "date" | "secret" | "file" |
default | string | number | boolean | string[] | null |
nullable | boolean |
uniqueItems | boolean |
integer | boolean |
minimum | number |
maximum | number |
exclusiveMinimum | number |
exclusiveMaximum | number |
minLength | integer |
maxLength | integer |
minItems | integer |
maxItems | integer |
required | boolean |
secret | boolean |
help | string |
placeholder | string |
options | { value, label, description, preview }[] |
other | boolean |
Action
| Field | Type |
|---|---|
id required | string |
label required | string |
values required | object |
ReviewBlock
| Field | Type |
|---|---|
type required | "code" | "diff" | "json" |
title | string |
requiresReview | boolean |
language | "text" | "sql" | "javascript" | "typescript" | "python" | "json" |
text required | string |
or
| Field | Type |
|---|---|
type required | "table" |
title | string |
requiresReview | boolean |
columns required | string[] |
rows required | string[][] |
or
| Field | Type |
|---|---|
type required | "image" |
title | string |
requiresReview | boolean |
attachmentId required | string |
alt required | string |
Attachment
| Field | Type |
|---|---|
id required | string |
name required | string |
size required | integer |
type required | string |
PublicKey
| Field | Type |
|---|---|
kty required | "RSA" |
n required | string |
e required | "AQAB" |
alg | "RSA-OAEP-256" |
ext | boolean |
key_ops | "encrypt"[] |
Envelope
| Field | Type |
|---|---|
key required | string |
iv required | string |
data required | string |
DestinationInput
| Field | Type |
|---|---|
key | string |
transport | { kind } | { kind, url } |
description | string |
types required | RequestType[] |
RequestType
| Field | Type |
|---|---|
id required | string |
label required | string |
description | string |
fields | Field[] |
kind | "session" |
session | { prompt, attachments, modes, model, effort, commands } |
SessionInput
| Field | Type |
|---|---|
prompt required | string |
modes | object |
model | string |
effort | string |
command | { id, arguments } |
attachments | string[] |
ResponseInput
| Field | Type |
|---|---|
values required | object |
actionId | string |
secrets | object |
ConnectionColor
| Field | Type |
|---|---|
mode required | "auto" |
or
| Field | Type |
|---|---|
mode required | "neutral" |
or
| Field | Type |
|---|---|
mode required | "custom" |
hue required | number |
saturation required | number |
SealedEnvelope
| Field | Type |
|---|---|
v required | 1 |
ctx required | { t, account } |
from required | string |
to required | { kid, enc, key }[] |
iv required | string |
ct required | string |
sig required | string |
SealedMessageInput
| Field | Type |
|---|---|
id required | string |
kind | "message" | "secret" |
sealed required | SealedEnvelope |
digest required | string |
asks required | boolean |
attachments | { id }[] |
taskId | string |
replyTo | string |
expiresAt | string |
expiresIn | number |
SealedResponseInput
| Field | Type |
|---|---|
sealed required | SealedEnvelope |
secrets | object |
files | string[] |
secretFiles | string[] |
SealedOutboxInput
| Field | Type |
|---|---|
id required | string |
destinationId required | string |
typeId required | string |
sealed required | SealedEnvelope |
files | string[] |
grants | { message, kid, enc, key }[] |
replyTo | string |
expiresIn | number |
AccountKeys
| Field | Type |
|---|---|
statement required | { v, account, identity, inbox, created } |
sig required | string |
Certificate
| Field | Type |
|---|---|
certificate required | { v, account, kid, sign, box, created } |
sig required | string |
WrappedKeyring
| Field | Type |
|---|---|
v required | 1 |
kind required | "prf" | "recovery" |
credentialId | string |
iv required | string |
ct required | string |
Error
| Field | Type |
|---|---|
error required | string |
code | string |
Ping
| Field | Type |
|---|---|
ok required | true |
channel required | string |
version required | string |
Message
| Field | Type |
|---|---|
id required | string |
title required | string |
body | string |
kind required | "message" | "form" | "report" | "files" | "secret" |
status required | "pending" | "answered" | "delivered" | "expired" | "cancelled" | "queued" | "running" | "completed" | "failed" |
direction required | "inbound" | "outbound" |
sender required | string |
senderId | string |
createdAt required | string |
revision | integer |
read | boolean |
fields | Field[] |
attachments | Attachment[] |
blocks | ReviewBlock[] |
actions | Action[] |
response | { values, actionId, secretFields, createdAt, sealed } |
expiresAt | string |
replyTo | string |
threadId | string |
threadSequence | integer |
runId | string |
secretDelivery | "offered" | "claimed" | "ready" | "received" |
destinationId | string |
typeId | string |
input | { destinationId, typeId, values, sealed } |
result | { status, completedAt, reason, sealed } |
sealed | SealedEnvelope |
Tombstone
| Field | Type |
|---|---|
id required | string |
revision required | integer |
deleted required | true |
Destination
| Field | Type |
|---|---|
id required | string |
integrationId required | string |
name required | string |
key | string |
signingSecret | string |
Account
| Field | Type |
|---|---|
id required | string |
email required | string |
plan required | string |
used required | number |
reserved | number |
credits | number |
storage required | number |
usageResetsAt | string |
Integration
| Field | Type |
|---|---|
id required | string |
name required | string |
scopes required | "messages:send" | "messages:read" | "destinations:manage" | "events:read" | "files:write"[] |
createdAt required | string |
color | ConnectionColor |
e2ee | boolean |
TokenInfo
| Field | Type |
|---|---|
id required | string |
name required | string |
scopes required | "messages:send" | "messages:read" | "destinations:manage" | "events:read" | "files:write"[] |
createdAt required | string |
lastUsedAt required | string | null |
account required | { email, name } |
TaskLog
| Field | Type |
|---|---|
sequence required | integer |
stream required | "stdout" | "stderr" |
text required | string |
sealed | SealedEnvelope |
KeyDirectory
| Field | Type |
|---|---|
account required | AccountKeys | null |
certificates required | { certificate, sig, integrationId }[] |
KeyBackup
| Field | Type |
|---|---|
id required | string |
wrapped required | WrappedKeyring |
createdAt required | string |
PendingDevice
| Field | Type |
|---|---|
id required | string |
label required | string |
state required | "waiting" | "challenged" | "revealed" | "approved" | "rejected" |
createdAt required | string |
expiresAt required | string |
commit required | string |
challenge | string |
transfer | string |
nonce | string |
envelope | SealedEnvelope |